diff --git a/.gitignore b/.gitignore index 1574c19..6799d45 100644 --- a/.gitignore +++ b/.gitignore @@ -152,5 +152,10 @@ ready* !/recordings/*.json /checkpoints/*/*episode_*.* -/checkpoints/*/latest_checkpoint.* +checkpoints/**/best_checkpoint.* +!/checkpoints/**/latest_checkpoint.* /archived_docs +/.spec-workflow +/runs +/metrics_history +/.pixi diff --git a/README.md b/README.md index 389150c..24648bd 100644 --- a/README.md +++ b/README.md @@ -15,24 +15,15 @@ The key features of OpenCDA-MARL are: * Scalability: Distributed training infrastructure supporting large-scale multi-agent scenarios with hundreds of vehicles. * Mixed Autonomy: Support for mixed traffic with human-driven vehicles, rule-based AVs, and learning-based agents. -Users can refer to our [documentation](#) for detailed guides on MARL integration, training procedures, and API references. For the original OpenCDA documentation, visit [OpenCDA documentation](https://opencda-documentation.readthedocs.io/en/latest/). +Users can refer to our [documentation](https://radar-lab.github.io/OpenCDA-MARL/) for detailed guides on MARL integration, training procedures, and API references. For the original OpenCDA documentation, visit [OpenCDA documentation](https://opencda-documentation.readthedocs.io/en/latest/). ## What's New in OpenCDA-MARL ### August 2025 -* **MARL Framework Integration**: Full integration of Multi-Agent Reinforcement Learning capabilities with support for PPO, SAC, QMIX, and MADDPG algorithms. -* **Distributed Training**: Scalable training infrastructure using Ray/RLlib for large-scale multi-agent scenarios. -* **Mixed Autonomy Support**: Seamless integration of learning-based agents with rule-based vehicles and human-driven traffic. - -### Key Updates from Original OpenCDA - -* **Environment Changes**: Changed Conda environment to Pixi for easy installation. -* **Enhanced Configuration System**: Clean YAML-based configuration with `default.yaml` template -* **Docker Support**: Easy deployment and reproducibility -* **Windows Compatibility**: Full support for Windows with Python 3.10.x and CUDA 12.8 -* **HD Map Manager**: Real-time rasterization maps for RL planning -* **CARLA 0.9.15**: Latest CARLA version support with improved stability +* **MARL Framework Integration**: Core Multi-Agent Reinforcement Learning framework with implemented algorithms including Q-learning, DQN, and TD3 for intersection management and cooperative driving tasks. +* **Training Infrastructure**: Single-agent training capabilities with experience replay and checkpoint management, with distributed Ray/RLlib training planned for future releases. +* **Mixed Autonomy Support**: Seamless integration of learning-based MARL agents with rule-based vehicles, vanilla behavior agents, and human-driven traffic. ## Major Components @@ -40,13 +31,12 @@ Users can refer to our [documentation](#) for detailed guides on MARL integratio OpenCDA-MARL extends the original four components with MARL-specific modules: -* MARL Training Framework: Distributed training infrastructure with multiple RL algorithms -* Cooperative Driving System: Enhanced with learning-based decision making -* Co-Simulation Tools: CARLA + SUMO integration with RL environment wrapper +* MARL Training Framework: Core training infrastructure with Q-learning, DQN, and TD3 algorithms for single-agent and multi-agent scenarios +* Cooperative Driving System: Enhanced with learning-based decision making for cooperative driving tasks * Data Manager and Repository: Training data collection and replay buffer management * Scenario Manager: MARL-specific training and evaluation scenarios -Check our [documentation](#) for detailed architecture and MARL integration. +Check our [documentation](https://radar-lab.github.io/OpenCDA-MARL/marl/architecture/) for detailed architecture and MARL integration. ## Get Started @@ -62,8 +52,8 @@ Note: We continuously improve the performance of OpenCDA-MARL. Currently, it is ### Developer Guide -* [Class Design](https://radar-lab.github.io/OpenCDA-MARL/architecture/) -* [Customize Your Algorithms](#) +* [Class Design](https://radar-lab.github.io/OpenCDA-MARL/marl/architecture/) +* [Customize Your Algorithms](https://radar-lab.github.io/OpenCDA-MARL/marl/algorithms/) * [API Reference](https://radar-lab.github.io/OpenCDA-MARL/api/opencda-marl/overview/)
### Contributing diff --git a/checkpoints/dqn_300vph/latest_checkpoint.pth b/checkpoints/dqn_300vph/latest_checkpoint.pth new file mode 100644 index 0000000..05e4979 Binary files /dev/null and b/checkpoints/dqn_300vph/latest_checkpoint.pth differ diff --git a/checkpoints/mappo_065_300vph/latest_checkpoint.pth b/checkpoints/mappo_065_300vph/latest_checkpoint.pth new file mode 100644 index 0000000..a7c2963 Binary files /dev/null and b/checkpoints/mappo_065_300vph/latest_checkpoint.pth differ diff --git a/checkpoints/mappo_300vph/latest_checkpoint.pth b/checkpoints/mappo_300vph/latest_checkpoint.pth new file mode 100644 index 0000000..e59429a Binary files /dev/null and b/checkpoints/mappo_300vph/latest_checkpoint.pth differ diff --git a/checkpoints/sac_300vph/latest_checkpoint.pth b/checkpoints/sac_300vph/latest_checkpoint.pth new file mode 100644 index 0000000..2839e81 Binary files /dev/null and b/checkpoints/sac_300vph/latest_checkpoint.pth differ diff --git a/checkpoints/td3_43d_300vph/latest_checkpoint.pth b/checkpoints/td3_43d_300vph/latest_checkpoint.pth new file mode 100644 index 0000000..cce36c0 Binary files /dev/null and b/checkpoints/td3_43d_300vph/latest_checkpoint.pth differ diff --git a/checkpoints/td3_44d_300vph/latest_checkpoint.pth b/checkpoints/td3_44d_300vph/latest_checkpoint.pth new file mode 100644 index 0000000..d8ce582 Binary files /dev/null and b/checkpoints/td3_44d_300vph/latest_checkpoint.pth differ diff --git a/checkpoints/td3_44d_400vph/latest_checkpoint.pth b/checkpoints/td3_44d_400vph/latest_checkpoint.pth new file mode 100644 index 0000000..898d6a7 Binary files /dev/null and b/checkpoints/td3_44d_400vph/latest_checkpoint.pth differ diff --git a/checkpoints/td3_8d_300vph_v2/latest_checkpoint.pth b/checkpoints/td3_8d_300vph_v2/latest_checkpoint.pth new file mode 100644 index 0000000..73a52a2 Binary files /dev/null and b/checkpoints/td3_8d_300vph_v2/latest_checkpoint.pth differ diff --git a/checkpoints/td3_8d_300vph_v3/latest_checkpoint.pth b/checkpoints/td3_8d_300vph_v3/latest_checkpoint.pth new file mode 100644 index 0000000..85ca3dd Binary files /dev/null and b/checkpoints/td3_8d_300vph_v3/latest_checkpoint.pth differ diff --git a/configs/marl/behavior.yaml b/configs/marl/behavior.yaml new file mode 100644 index 0000000..3904d1b --- /dev/null +++ b/configs/marl/behavior.yaml @@ -0,0 +1,9 @@ +description: |- + Behavior-based agent for intersection scenario. + Traditional agent using OpenCDA BehaviorAgent - no training required. + +agents: + agent_type: "behavior" + + # Uses agents.vehicle.behavior config from default.yaml + # BehaviorAgent inherits vehicle behavior settings diff --git a/configs/marl/default.yaml b/configs/marl/default.yaml index 551a491..2592e21 100644 --- a/configs/marl/default.yaml +++ b/configs/marl/default.yaml @@ -1,7 +1,8 @@ description: |- - MARL default configuration that other MARL configurations can inherit from. + MARL default configuration - contains common settings shared by all agents. + Agent-specific configurations inherit from this file via OmegaConf merge. -# Base OpenCDA configuration +# Base OpenCDA world configuration world: sync_mode: true host: 127.0.0.1 @@ -13,54 +14,138 @@ world: sun_altitude_angle: 15 # 90 is the midday and -90 is the midnight cloudiness: 0 # 0 is the clean sky and 100 is the thickest cloud precipitation: 0 # rain, 100 is the heaviest rain - precipitation_deposits: 0 # Determines the creation of puddles. Values range from 0 to 100, being 0 none at all and 100 a road completely capped with water. + precipitation_deposits: 0 # Determines the creation of puddles. Values range from 0 to 100 wind_intensity: 0 # it will influence the rain fog_density: 0 # fog thickness, 100 is the largest fog_distance: 0 # Fog start distance. Values range from 0 to infinite. - fog_falloff: 0 # Density of the fog (as in specific mass) from 0 to infinity. The bigger the value, the more dense and heavy it will be, and the fog will reach smaller heights + fog_falloff: 0 # Density of the fog from 0 to infinity wetness: 0 +# Spectator view preset +spectator: + preset: "intersection_bird_eye" + +# Scenario metadata +meta: + scenario_type: "intersection" + town: "intersection_12_lane_fixed" + +# Scenario configuration (shared across all agents) scenario: + simulation: + max_steps: 2400 # 2 minute = 20fps * 60s * 2 + max_episodes: 500 + traffic: + # Traffic mode: 'record', 'replay', or 'live' + mode: "replay" + replay_file: "recordings/traffic_200vph.json" + base_speed: 55.0 # km/h base speed for vehicles + active_junctions: [4] + # Vehicle Type Configuration - # Using inclusion-based approach for consistent collision detection and simulation - # Recommended: Similar-sized sedans for consistent physics and collision detection - # ENABLED VEHICLES included_vehicle_types: - - "vehicle.audi.a2" # Compact sedan - - "vehicle.bmw.grandtourer" # Family sedan - - "vehicle.citroen.c3" # Compact car - - "vehicle.lincoln.mkz_2017" # Luxury sedan - - "vehicle.seat.leon" # Compact sedan - # DISABLED VEHICLES (Choose either included_vehicle_types or excluded_vehicle_types) + - "vehicle.audi.a2" + - "vehicle.bmw.grandtourer" + - "vehicle.citroen.c3" + - "vehicle.lincoln.mkz_2017" + - "vehicle.seat.leon" excluded_vehicle_types: - "walker" -# Agent behavior configuration (based on OpenCDA behavior_agent) -# This config is used for all agent types (traffic, RL agents, rule-based agents) + planner: + distance: 5.0 # base spacing (m) + spawn_offset: 7 # multiples of safe_distance upstream + dest_offset: 7 # multiples of safe_distance downstream + spawn_z_lift: 0.3 # small lift to avoid ground collision + wp_step: 1.0 # stepping granularity along lanes (m) + allow_uturn: false + visualize: + entry_wp: false + exit_wp: false + spawn_wp: false + dest_wp: false + route_line: false + junction_center: true + bbox: false + extent_box: true + text: true + life_time: 10.0 + + flows: + - name: "north" + rate_vph: 300 + lanes: [0, 1, 2] + start_step: 0 + end_step: 2400 + direction: "north" + speed_variation: 0.2 + middle_peak: + intensity: 0.4 + position: 0.5 + width: 0.3 + + - name: "south" + rate_vph: 300 + lanes: [0, 1, 2] + start_step: 0 + end_step: 2400 + direction: "south" + speed_variation: 0.15 + middle_peak: + intensity: 0.3 + position: 0.4 + width: 0.4 + + - name: "east" + rate_vph: 300 + lanes: [0, 1, 2] + start_step: 0 + end_step: 2400 + direction: "east" + speed_variation: 0.1 + middle_peak: + intensity: 0.35 + position: 0.6 + width: 0.25 + + - name: "west" + rate_vph: 300 + lanes: [0, 1, 2] + start_step: 0 + end_step: 2400 + direction: "west" + speed_variation: 0.25 + middle_peak: + intensity: 0.5 + position: 0.45 + width: 0.35 + +# Base vehicle configuration (sensing, localization - shared by all agents) agents: - # Vehicle configuration for VehicleManager + debug: false + vehicle: sensing: perception: - activate: false # when not activated, objects positions will be retrieved from server directly + activate: false camera: - visualize: 0 # how many camera images need to be visualized. 0 means no visualization for camera - num: 1 # how many cameras are mounted on the vehicle - positions: [[2.5, 0, 1.0, 0]] # relative positions (x,y,z,yaw) of the camera - lidar: # lidar sensor configuration, check CARLA sensor reference for more details + visualize: 0 + num: 1 + positions: [[2.5, 0, 1.0, 0]] + lidar: visualize: false localization: - activate: true # when not activated, ego position will be retrieved from server directly - use_kalman: false # Disable Kalman filter to prevent route following lag - dt: 0.1 # used for kalman filter + activate: true + use_kalman: false + dt: 0.1 debug_helper: show_animation: false map_manager: - visualize: false # Enable BEV visualization (shows cv2 window for each vehicle) - activate: false # Activate map manager for BEV generation + visualize: false + activate: false v2x: enabled: true @@ -71,110 +156,62 @@ agents: behavior: debug: false - max_speed: 45 # maximum speed, km/h (~30 mph - typical US arterial intersection speed) - # when a vehicles needs to be close to another vehicle asap - tailgate_speed: 50 - # max_speed - speed_lim_dist = target speed - speed_lim_dist: 5 - # used in car following mode to decrease speed for distance keeping + max_speed: 45 + tailgate_speed: 50 + speed_lim_dist: 5 speed_decrease: 15 - # ttc safety thresholding for decreasing speed safety_time: 4 - # used to identify whether a emergency stop needed emergency_param: 0.4 - # whether overtake allowed, typically false for platoon leader - overtake_allowed: true - # used for collision checking + overtake_allowed: true collision_time_ahead: 1.5 - # the vehicle can not do another overtake during next certain steps - overtake_counter_recover: 35 - # whether to ignore traffic light - ignore_traffic_light: false # respect traffic lights in intersection scenarios - # the unit distance between two adjacent waypoints in meter - sample_resolution: 1.0 # waypoint distance in meters (finer for intersection navigation) - - # Local planner configuration (required by OpenCDA BehaviorAgent) - local_planner: - buffer_size: 12 # waypoint buffer size - trajectory_update_freq: 15 # trajectory points updating frequency - waypoint_update_freq: 9 # waypoint updating frequency - min_dist: 2 # pop waypoints too close to current location (smaller for intersections) - trajectory_dt: 0.10 # trajectory sampling time (faster for intersection scenarios) - debug: true # whether to draw future/history waypoints - debug_trajectory: true # whether to draw trajectory points and path - - vanilla: - debug: false - max_speed: 45 # maximum speed, km/h - emergency_param: 0.4 # used to identify whether a emergency stop needed - ignore_traffic_light: true # whether to ignore traffic light - collision_time_ahead: 1.5 # used for collision checking - - local_planner: # trajectory planning related - trajectory_update_freq: 9 # trajectory points updating frequency - debug: false # whether to draw future/history waypoints - debug_trajectory: true # whether to draw the trajectory points and path - - # RuleBasedAgent configuration (3-stage rule-based with JAIR enhancements) - rule_based: - debug: false - debug_rules: false - max_speed: 65 # Maximum speed (km/h) - matches agent_behavior for consistency - emergency_param: 0.4 # used to identify whether a emergency stop needed - ignore_traffic_light: true # whether to ignore traffic light - collision_time_ahead: 1.5 # used for collision checking - local_planner: # trajectory planning related - trajectory_update_freq: 5 # trajectory points updating frequency - debug: false # whether to draw future/history waypoints - debug_trajectory: true # whether to draw the trajectory points and path - - # Stage 1: Junction Management (Enhanced with trajectory prediction & dynamic speed adjustment) - junction_approach_distance: 70.0 # Distance to start junction management (meters) - junction_conflict_distance: 50.0 # Distance to check for conflicting vehicles (meters) - junction_speed_factor: 0.6 # Reduce speed to 60% at junctions (dynamic adjustment) - junction_min_reduction: 30.0 # Minimum speed reduction at junctions (km/h) - #~CRITICAL: previous 15.0 achieves 75%/25% success/collision rate - # junction_min_reduction: 15.0 # Minimum speed reduction at junctions (km/h) - - - # Trajectory-based conflict detection (replaces heading difference) - #~CRITICAL: previous: 3.0/2.0/1.0 achieves 75%/25% success/collision rate - trajectory_lookahead_time: 8.0 # Time horizon for trajectory prediction (seconds) - ttc_threshold: 5.0 # Minimum TTC difference for conflict detection (seconds) - trajectory_step_size: 3.0 # Step size for trajectory prediction (meters) - - # Stage 2: Car Following (Enhanced with leading vehicle speed consideration) - #~CRITICAL: previous speed buffer/time_headway are 5.0/2.0 - #~ achieves 75%/25% success/collision rate - following_speed_buffer: 8.0 # Speed buffer below leading vehicle (km/h) - time_headway: 3.0 # Time headway for safe following (seconds) - following_gain: 0.5 # Car following controller gain - minimum_distance_buffer: 5.0 # Minimum physical distance buffer (meters) - same_lane_tolerance_deg: 30 # Heading tolerance for same lane detection (degrees) - front_cone_angle_deg: 45 # Front detection cone angle (degrees) - - # Stage 3: Cruising - #~CRITICAL: previous: 0.95 achieves 75%/25% success/collision rate - #~Increase to 1.0 to faster when cruising - cruising_speed_factor: 1.0 # 95% of vanilla when cruising + overtake_counter_recover: 35 + ignore_traffic_light: false + sample_resolution: 1.0 + local_planner: + buffer_size: 12 + trajectory_update_freq: 15 + waypoint_update_freq: 9 + min_dist: 2 + trajectory_dt: 0.10 + debug: true + debug_trajectory: true # Evaluation System Configuration evaluation: - # Evaluation modes: - # - 'full': Detailed time-series tracking with plotting capabilities - # - 'lightweight': Summary-only tracking for efficient training - # - 'disabled': No evaluation tracking mode: "full" root_dir: "evaluation_outputs" - - # Data management - save_interval: 1 # Save every N episodes - compression: true # Use compression for data storage - - # Memory management - keep_last_n_files: 0 # Keep only last N episode files (0 = keep all) - keep_summaries: 1000 # Number of episode summaries to retain in memory (lightweight mode) - - # Visualization settings - plot_interval: 5 # Generate plots every N episodes (full mode only) + save_interval: 1 + compression: true + keep_last_n_files: 0 + keep_summaries: 1000 + plot_interval: 5 + +# TensorBoard Configuration +tensorboard: + enabled: true + log_dir: "runs" + log_frequency: 1 + metrics: + losses: true + q_values: true + buffer: true + episode: true + rewards: true + +# MARL training base configuration +MARL: + training: + metrics_export_interval: 5 + metrics_export_dir: "metrics_history" + +# CARLA World Reset Configuration +world_reset: + reset_frequency: 10 + auto_reset: + enabled: true + slowdown_threshold: 1.5 + monitoring_window: 100 + min_epochs_before_auto: 3 + log_reset_events: true + log_step_times: true diff --git a/configs/marl/dqn.yaml b/configs/marl/dqn.yaml new file mode 100644 index 0000000..28b52fe --- /dev/null +++ b/configs/marl/dqn.yaml @@ -0,0 +1,129 @@ +# DQN (Deep Q-Network) Configuration +# Reference: "Playing Atari with Deep Reinforcement Learning" - Mnih et al., 2013 +# "Human-level control through deep reinforcement learning" - Nature, 2015 +# +# Citation: +# @article{mnih2015human, +# title={Human-level control through deep reinforcement learning}, +# author={Mnih, Volodymyr and others}, +# journal={Nature}, +# volume={518}, +# pages={529--533}, +# year={2015} +# } + +description: |- + DQN (Deep Q-Network) for autonomous intersection management. + Uses discrete speed actions with neural network Q-value approximation, + experience replay, and target networks for stable learning. + Configured for 8D state space to match other algorithms for fair comparison. + +_extend_fields: + - "world.seed" + - "MARL.training.metrics_export_interval" + - "MARL.training.metrics_export_dir" + +world: + seed: 5 # Added to default seed (11) = 16 total + +agents: + agent_type: "marl" + + marl: + debug: false + max_speed: 65 # km/h + emergency_param: 0.4 + ignore_traffic_light: true + collision_time_ahead: 1.5 + + local_planner: + trajectory_update_freq: 5 + debug: false + debug_trajectory: false + +MARL: + algorithm: "dqn" + + # 8D state space (same as TD3/MAPPO/SAC for fair comparison) + state_dim: 8 + + dqn: + # ============ Feature Configuration (8D) ============ + features: + rel_x: 1 + rel_y: 1 + speed: 1 + heading_angle: 1 + dist_to_intersection: 1 + dist_to_front_vehicle: 1 + lane_position: 1 + waypoint_buffer: 1 + + # ============ Action Space ============ + # 4 discrete speed actions (km/h) + # - 15: Very slow (yielding/cautious approach) + # - 30: Slow (moderate caution) + # - 45: Medium (normal driving) + # - 60: Fast (clear path) + speed_actions: [15, 30, 45, 60] + + # ============ Network Architecture ============ + # Larger network for 8D state space + hidden_dims: [256, 128] + + # ============ Exploration Parameters ============ + epsilon: 0.3 # Initial exploration rate + epsilon_decay: 0.995 # Decay per episode + epsilon_min: 0.05 # Minimum exploration rate + + # ============ Learning Parameters ============ + learning_rate: 0.001 + discount_factor: 0.99 + + # ============ Experience Replay ============ + memory_size: 25000 + batch_size: 256 + + # ============ Target Network ============ + target_update_freq: 100 + + training: + training_mode: false + checkpoint_dir: "checkpoints/" + save_freq: 5 + keep_checkpoints: 3 + load_checkpoint: "checkpoints/dqn_300vph/latest_checkpoint.pth" + + # ============ Rewards ============ + rewards: + # Terminal rewards + collision: -500.0 + success: 400.0 + + # Per-step rewards + step_penalty: -0.8 + speed_bonus: 0.3 + speed_threshold: 45.0 + + # TTC-based safety reward (DISABLED) + ttc_safe_threshold: 4.0 + ttc_max_penalty: 0.0 + ttc_decay_rate: 1.5 + ttc_near_miss_threshold: 2.0 + + # Clearance speed bonus + clearance_speed_bonus: 0.3 + clearance_threshold: 30.0 + + # Progress reward + progress_scale: 0.4 + junction_threshold: 5.0 + + # Stop penalty + stop_threshold: 3.0 + stop_penalty: -1.5 + + # Yielding bonus + yielding_bonus: 0.5 + yielding_ttc_threshold: 3.0 + yielding_speed_drop: 5.0 diff --git a/configs/marl/intersection.yaml b/configs/marl/intersection.yaml deleted file mode 100644 index 4ee84f1..0000000 --- a/configs/marl/intersection.yaml +++ /dev/null @@ -1,224 +0,0 @@ -# Extend fields - only these fields will extend instead of replace -_extend_fields: - - "world.seed" # Add to base seed for variation - -description: |- - DQN configuration for 4-way intersection with HIGH traffic density. - Deep Q-Network with continuous states and neural network approximation. - This scenario focuses on intersection navigation under heavy congestion. - -# Demonstrate seed extension - adds variation to base seed -world: - seed: 5 # This will be ADDED to default.yaml seed (11) = 16 total - -spectator: - preset: "intersection_bird_eye" - -meta: - # available scenario types: intersection - scenario_type: "intersection" - town: "intersection_12_lane_fixed" - # custom xodr path for this scenario - # xodr_path: "opencda_marl/assets/maps/intersection.xodr" - -# minimal scenario configuration -scenario: - simulation: - max_steps: 2400 # 2 minute = 20fps * 60s * 2 - max_episodes: 100 - traffic: - # Traffic mode: 'record', 'replay', or 'live' - mode: "replay" - replay_file: "recordings/lite_2minL.json" - #replay_file: "recordings/quick_test.json" - base_speed: 45.0 # km/h base speed for vehicles - active_junctions: [4] - - planner: - distance: 5.0 # base spacing (m) - spawn_offset: 7 # multiples of safe_distance upstream - dest_offset: 7 # multiples of safe_distance downstream - spawn_z_lift: 0.3 # small lift to avoid ground collision - wp_step: 1.0 # stepping granularity along lanes (m) - allow_uturn: false - visualize: - entry_wp: false - exit_wp: false - spawn_wp: false - dest_wp: false - route_line: false - junction_center: true - bbox: false - extent_box: true - text: true - life_time: 10.0 - - flows: - - name: "north" - rate_vph: 200 - lanes: [0, 1, 2] - start_step: 0 - end_step: 2400 - direction: "north" - speed_variation: 0.2 - middle_peak: - intensity: 0.4 # How much denser the peak is (0.0 = no peak, 1.0 = very dense) - position: 0.5 # Where peak occurs (0.5 = middle of simulation) - width: 0.3 # How wide the peak is (0.1 = narrow, 0.5 = wide) - - - name: "south" - rate_vph: 200 - lanes: [0, 1, 2] - start_step: 0 - end_step: 2400 - direction: "south" - speed_variation: 0.15 - middle_peak: - intensity: 0.3 - position: 0.4 - width: 0.4 - - - name: "east" - rate_vph: 200 - lanes: [0, 1, 2] - start_step: 0 - end_step: 2400 - direction: "east" - speed_variation: 0.1 - middle_peak: - intensity: 0.35 - position: 0.6 - width: 0.25 - - - name: "west" - rate_vph: 200 - lanes: [0, 1, 2] - start_step: 0 - end_step: 2400 - direction: "west" - speed_variation: 0.25 - middle_peak: - intensity: 0.5 - position: 0.45 - width: 0.35 - -agents: - debug: false - agent_type: "marl" - - marl: - # Base VanillaAgent parameters - debug: false - max_speed: 65 # Maximum speed for the agent (km/h) - emergency_param: 0.4 - ignore_traffic_light: true - collision_time_ahead: 1.5 - local_planner: - trajectory_update_freq: 5 - debug: false - debug_trajectory: false # Keep trajectory clean for RL - -MARL: - # Algorithm selection: q_learning, td3, sac, ppo, dqn - algorithm: "td3" - - state_dim: 9 # Auto-calculated from custom features (9D total) - action_dim: 1 # Continuous speed control - - # TD3 Algorithm Configuration (Multi-Agent) - td3: - # Feature configuration (9D total) - features: - rel_x: 1 # Relative X to intersection - rel_y: 1 # Relative Y to intersection - position_x: 1 # Absolute X position - position_y: 1 # Absolute Y position - lane_position: 1 # Lane position (0=intersection, 1=left, 2=middle, 3=right) - heading_angle: 1 # Vehicle orientation (radians) - dist_to_intersection: 1 # Distance to intersection - dist_to_front_vehicle: 1 # Distance to front vehicle - waypoint_buffer: 1 # Waypoint count (0-50 range) - - # Alternative: Use one-hot encoding for lane_position (12D total) - # features: - # rel_x: 1 - # rel_y: 1 - # position_x: 1 - # position_y: 1 - # lane_position_onehot: 4 # One-hot: [at_intersection, left, middle, right] - # heading_angle: 1 - # dist_to_intersection: 1 - # dist_to_front_vehicle: 1 - # waypoint_buffer: 1 - - # LSTM conflict encoder (from ITS_Sim architecture) - conflict_encoder: - type: "LSTM" - input_size: 9 # From custom features (auto-calculated) - hidden_size: 256 # LSTM hidden dimension - num_layers: 1 # LSTM layers - - # Motion planner architecture (from AdvRAIM) - motion_planner: - num_layers: 5 - input_dim: [265, 1024, 1024, 512, 256] # First: 9 + 256 = 265 - output_dim: [1024, 1024, 512, 256, 1] # Final output: 1 (action) - - # Learning rates - reduced for stable training - learning_rate_actor: 1e-3 # Actor learning rate (stable learning) - learning_rate_critic: 1e-3 # Critic learning rate (stable convergence) - - # TD3 hyperparameters - tau: 0.01 # Soft update parameter for target networks (2x faster updates) - discount: 0.99 # Discount factor (gamma) - policy_noise: 0.2 # Noise added to target policy during critic update - noise_clip: 0.3 # Range to clip target policy noise - exploration_noise: 0.5 # Maximum noise for aggressive speed exploration (±21 km/h) - policy_freq: 2 # Delayed policy updates (update actor every 2 critic updates) - - # Training parameters - batch_size: 256 # Batch size for training - - # Smart replay buffer configuration - memory_size: 25000 # Sized for >1 episode (~14k) to prevent FIFO losses - recency_ratio: 0.5 # 50% samples from recent 20% of buffer - - # Auto-clear configuration (prevents stale experiences and FIFO losses) - clear_episodes: 1 # Clear buffer after EVERY episode (prevents cap overflow) - clear_keep_ratio: 0.6 # Keep 60% newest experiences (9k from 15k) - # Alternative: clear_interval: 10000 # Clear every 10k transitions - - # Prioritized Experience Replay (PER) configuration - use_per: true # Enable PER (TD-error based sampling) - per_alpha: 0.6 # Priority exponent (0=uniform, 1=full priority) - per_beta: 0.4 # Initial importance sampling correction - per_beta_increment: 0.001 # Beta annealing rate (reaches 1.0 gradually) - - # Action bounds (continuous speed control in km/h) - prevents traffic jams - max_action: 65.0 # Maximum speed - min_action: 30.0 # Minimum speed (prevents stopping) - warmup_steps: 500 # Reduced warmup for faster TD3 takeover - - # Training configuration - training: - # Training control - training_mode: true # Set to false for evaluation only - - # Checkpoint settings - checkpoint_dir: "checkpoints/" - save_freq: 1 - keep_checkpoints: 3 - - # Resume training - #load_checkpoint: null # Path to checkpoint to resume from (null = start fresh) - load_checkpoint: "checkpoints/td3/latest_checkpoint.pth" # Example - - rewards: - collision: -500.0 # Severe penalty to ensure safety priority - success: 400.0 # Higher reward for reaching destination - step_penalty: -1.5 # Stronger time pressure to encourage faster completion - speed_bonus: 0.5 # Doubled bonus for maintaining speed > threshold - speed_threshold: 45.0 # Lowered threshold (km/h) for easier bonus achievement - # Optional reward shaping - # safe_distance_bonus: 0.1 # Small bonus for maintaining safe distance - # risky_distance_penalty: -0.5 # Small penalty for getting too close diff --git a/configs/marl/intersection_behavior.yaml b/configs/marl/intersection_behavior.yaml deleted file mode 100644 index 4ed8a98..0000000 --- a/configs/marl/intersection_behavior.yaml +++ /dev/null @@ -1,57 +0,0 @@ -# Extend fields - only these fields will extend instead of replace -_extend_fields: - - "world.seed" # Add to base seed for variation - -description: |- - MARL configuration for 4-way intersection with HIGH traffic density. - This scenario focuses on intersection navigation under heavy congestion. - -# Demonstrate seed extension - adds variation to base seed -world: - seed: 5 # This will be ADDED to default.yaml seed (11) = 16 total - -spectator: - preset: "intersection_bird_eye" - -meta: - # available scenario types: intersection - scenario_type: "intersection" - town: "intersection_12_lane_fixed" - # custom xodr path for this scenario - # xodr_path: "opencda_marl/assets/maps/intersection.xodr" - -# minimal scenario configuration -scenario: - simulation: - max_steps: 2400 # 2 minute = 20fps * 60s * 2 - max_episodes: 100 - traffic: - # Traffic mode: 'record', 'replay', or 'live' - mode: "replay" - replay_file: "recordings/lite_2minL.json" - #replay_file: "recordings/quick_test.json" - base_speed: 45.0 # km/h base speed for vehicles - active_junctions: [4] - - planner: - distance: 5.0 # base spacing (m) - spawn_offset: 7 # multiples of safe_distance upstream - dest_offset: 7 # multiples of safe_distance downstream - spawn_z_lift: 0.3 # small lift to avoid ground collision - wp_step: 1.0 # stepping granularity along lanes (m) - allow_uturn: false - visualize: - entry_wp: false - exit_wp: false - spawn_wp: false - dest_wp: false - route_line: false - junction_center: true - bbox: false - extent_box: true - text: true - life_time: 10.0 - -agents: - debug: false - agent_type: "behavior" diff --git a/configs/marl/intersection_dqn.yaml b/configs/marl/intersection_dqn.yaml deleted file mode 100644 index e16366e..0000000 --- a/configs/marl/intersection_dqn.yaml +++ /dev/null @@ -1,119 +0,0 @@ -# Extend fields - only these fields will extend instead of replace -_extend_fields: - - "world.seed" # Add to base seed for variation - -description: |- - DQN configuration for 4-way intersection with HIGH traffic density. - Deep Q-Network with continuous states and neural network approximation. - This scenario focuses on intersection navigation under heavy congestion. - -# Demonstrate seed extension - adds variation to base seed -world: - seed: 5 # This will be ADDED to default.yaml seed (11) = 16 total - -spectator: - preset: "intersection_bird_eye" - -meta: - # available scenario types: intersection - scenario_type: "intersection" - town: "intersection_12_lane_fixed" - # custom xodr path for this scenario - # xodr_path: "opencda_marl/assets/maps/intersection.xodr" - -# minimal scenario configuration -scenario: - simulation: - max_steps: 2400 # 2 minute = 20fps * 60s * 2 - max_episodes: 100 - traffic: - # Traffic mode: 'record', 'replay', or 'live' - mode: "replay" - replay_file: "recordings/lite_2minL.json" - #replay_file: "recordings/quick_test.json" - base_speed: 45.0 # km/h base speed for vehicles - active_junctions: [4] - - planner: - distance: 5.0 # base spacing (m) - spawn_offset: 7 # multiples of safe_distance upstream - dest_offset: 7 # multiples of safe_distance downstream - spawn_z_lift: 0.3 # small lift to avoid ground collision - wp_step: 1.0 # stepping granularity along lanes (m) - allow_uturn: false - visualize: - entry_wp: false - exit_wp: false - spawn_wp: false - dest_wp: false - route_line: false - junction_center: true - bbox: false - extent_box: true - text: true - life_time: 10.0 - -agents: - debug: false - agent_type: "marl" - - marl: - # Base VanillaAgent parameters - debug: false - max_speed: 65 # Maximum speed for the agent (km/h) - emergency_param: 0.4 - ignore_traffic_light: true - collision_time_ahead: 1.5 - local_planner: - trajectory_update_freq: 5 - debug: false - debug_trajectory: false # Keep trajectory clean for RL - -MARL: - # Algorithm selection: q_learning, td3, sac, ppo, dqn - algorithm: "dqn" - - state_dim: 7 # rel_x, rel_y, speed, heading_angle, dist_intersection, dist_front, lane_pos - - dqn: - # Speed actions (km/h) - discrete action space for DQN - speed_actions: [30, 45, 60] - - # Neural network architecture - hidden_dims: [64, 32] # Two hidden layers: input->64->32->output - - # DQN training parameters - epsilon: 0.2 # Higher initial exploration for neural network - epsilon_decay: 0.995 - epsilon_min: 0.01 - learning_rate: 0.003 # Lower learning rate for neural networks - discount_factor: 0.95 - - # Experience replay (always enabled for DQN) - memory_size: 10000 - batch_size: 128 - - # Target network update frequency - target_update_freq: 50 # Update target network every 100 training steps - - # Training configuration - training: - # Training control - training_mode: true # Set to false for evaluation only - - # Checkpoint settings - checkpoint_dir: "checkpoints/" - save_freq: 5 # Save checkpoint every 5 episodes (DQN needs more episodes to converge) - keep_checkpoints: 10 # Keep more checkpoints for DQN analysis - - # Resume training (start fresh for DQN - different from Q-learning checkpoints) - #load_checkpoint: null # Path to checkpoint to resume from (null = start fresh) - load_checkpoint: "checkpoints/dqn/best_checkpoint.pth" # Example - - rewards: - collision: -130.0 # Heavy penalty for collisions - success: 130.0 # High reward for reaching destination - step_penalty: -0.5 # Small penalty to encourage faster completion - # Optional reward shaping - # safe_distance_bonus: 0.1 # Small bonus for maintaining safe distance - # risky_distance_penalty: -0.5 # Small penalty for getting too close diff --git a/configs/marl/intersection_qcomplex.yaml b/configs/marl/intersection_qcomplex.yaml deleted file mode 100644 index 673b61e..0000000 --- a/configs/marl/intersection_qcomplex.yaml +++ /dev/null @@ -1,119 +0,0 @@ -# Extend fields - only these fields will extend instead of replace -_extend_fields: - - "world.seed" # Add to base seed for variation - -description: |- - MARL configuration for 4-way intersection with HIGH traffic density. - This scenario focuses on intersection navigation under heavy congestion. - -# Demonstrate seed extension - adds variation to base seed -world: - seed: 5 # This will be ADDED to default.yaml seed (11) = 16 total - -spectator: - preset: "intersection_bird_eye" - -meta: - # available scenario types: intersection - scenario_type: "intersection" - town: "intersection_12_lane_fixed" - # custom xodr path for this scenario - # xodr_path: "opencda_marl/assets/maps/intersection.xodr" - -# minimal scenario configuration -scenario: - simulation: - max_steps: 2400 # 2 minute = 20fps * 60s * 2 - max_episodes: 100 - traffic: - # Traffic mode: 'record', 'replay', or 'live' - mode: "replay" - replay_file: "recordings/lite_2minL.json" - #replay_file: "recordings/quick_test.json" - base_speed: 45.0 # km/h base speed for vehicles - active_junctions: [4] - - planner: - distance: 5.0 # base spacing (m) - spawn_offset: 7 # multiples of safe_distance upstream - dest_offset: 7 # multiples of safe_distance downstream - spawn_z_lift: 0.3 # small lift to avoid ground collision - wp_step: 1.0 # stepping granularity along lanes (m) - allow_uturn: false - visualize: - entry_wp: false - exit_wp: false - spawn_wp: false - dest_wp: false - route_line: false - junction_center: true - bbox: false - extent_box: true - text: true - life_time: 10.0 - -agents: - debug: false - agent_type: "marl" - - marl: - # Base VanillaAgent parameters - debug: false - max_speed: 65 # Maximum speed for the agent (km/h) - emergency_param: 0.4 - ignore_traffic_light: true - collision_time_ahead: 1.5 - local_planner: - trajectory_update_freq: 5 - debug: false - debug_trajectory: false # Keep trajectory clean for RL - -MARL: - # Algorithm selection: q_learning, td3, sac, ppo - algorithm: "q_learning" - - q_learning: - # speed actions (0-60 km/h) - speed_actions: [0, 10, 20, 30, 40, 50, 60] - - # State discretization (no traffic lights) - state_features: - distance_to_intersection: - bins: [0, 10, 30, 50, 100] # 5 bins: very close, close, medium, far, very far - speed: - bins: [0, 25, 45, 65] # 4 bins: stopped, slow, medium, fast - distance_to_front_vehicle: - bins: [0, 5, 15, 30] # 5 bins: very close, close, safe, far, no vehicle ahead (999.0) - lane_position: - bins: [-3, -2, -1, 0, 1, 2, 3] # 7 bins: exit right, exit center, exit left, junction, entry left, entry center, entry right - - # Learning parameters - epsilon: 0.1 - learning_rate: 0.1 - discount_factor: 0.95 - - use_experience_replay: false - memory_size: 1000 - batch_size: 32 - - # Training configuration - training: - # Training control - training_mode: true # Set to false for evaluation only - - # Checkpoint settings - checkpoint_dir: "checkpoints/" - save_freq: 1 # Save checkpoint every N episodes - keep_checkpoints: 5 # Keep last N episode checkpoints - - # Resume training - #load_checkpoint: null # Path to checkpoint to resume from (null = start fresh) - load_checkpoint: "checkpoints/q_learning/best_checkpoint.pkl" # Example - - rewards: - collision: -130.0 # Heavy penalty for collisions - success: 130.0 # High reward for reaching destination - step_penalty: -0.5 # Small penalty to encourage faster completion - # Optional reward shaping - # safe_distance_bonus: 0.1 # Small bonus for maintaining safe distance - # risky_distance_penalty: -0.5 # Small penalty for getting too close diff --git a/configs/marl/intersection_rule_based.yaml b/configs/marl/intersection_rule_based.yaml deleted file mode 100644 index a72b3f7..0000000 --- a/configs/marl/intersection_rule_based.yaml +++ /dev/null @@ -1,57 +0,0 @@ -# Extend fields - only these fields will extend instead of replace -_extend_fields: - - "world.seed" # Add to base seed for variation - -description: |- - MARL configuration for 4-way intersection with HIGH traffic density. - This scenario focuses on intersection navigation under heavy congestion. - -# Demonstrate seed extension - adds variation to base seed -world: - seed: 5 # This will be ADDED to default.yaml seed (11) = 16 total - -spectator: - preset: "intersection_bird_eye" - -meta: - # available scenario types: intersection - scenario_type: "intersection" - town: "intersection_12_lane_fixed" - # custom xodr path for this scenario - # xodr_path: "opencda_marl/assets/maps/intersection.xodr" - -# minimal scenario configuration -scenario: - simulation: - max_steps: 2400 # 2 minute = 20fps * 60s * 2 - max_episodes: 100 - traffic: - # Traffic mode: 'record', 'replay', or 'live' - mode: "replay" - replay_file: "recordings/lite_2minL.json" - #replay_file: "recordings/quick_test.json" - base_speed: 45.0 # km/h base speed for vehicles - active_junctions: [4] - - planner: - distance: 5.0 # base spacing (m) - spawn_offset: 7 # multiples of safe_distance upstream - dest_offset: 7 # multiples of safe_distance downstream - spawn_z_lift: 0.3 # small lift to avoid ground collision - wp_step: 1.0 # stepping granularity along lanes (m) - allow_uturn: false - visualize: - entry_wp: false - exit_wp: false - spawn_wp: false - dest_wp: false - route_line: false - junction_center: true - bbox: false - extent_box: true - text: true - life_time: 10.0 - -agents: - debug: false - agent_type: "rule_based" diff --git a/configs/marl/intersection_td3.yaml b/configs/marl/intersection_td3.yaml deleted file mode 100644 index e6b228a..0000000 --- a/configs/marl/intersection_td3.yaml +++ /dev/null @@ -1,130 +0,0 @@ -# Extend fields - only these fields will extend instead of replace -_extend_fields: - - "world.seed" # Add to base seed for variation - -description: |- - TD3 configuration for 4-way intersection with HIGH traffic density. - Twin Delayed DDPG with continuous action space for speed control. - This scenario focuses on intersection navigation under heavy congestion. - -# Demonstrate seed extension - adds variation to base seed -world: - seed: 5 # This will be ADDED to default.yaml seed (11) = 16 total - -spectator: - preset: "intersection_bird_eye" - -meta: - # available scenario types: intersection - scenario_type: "intersection" - town: "intersection_12_lane_fixed" - # custom xodr path for this scenario - # xodr_path: "opencda_marl/assets/maps/intersection.xodr" - -# minimal scenario configuration -scenario: - simulation: - max_steps: 2400 # 2 minute = 20fps * 60s * 2 - max_episodes: 100 - traffic: - # Traffic mode: 'record', 'replay', or 'live' - mode: "replay" - replay_file: "recordings/lite_2minL.json" - #replay_file: "recordings/quick_test.json" - base_speed: 45.0 # km/h base speed for vehicles - active_junctions: [4] - - planner: - distance: 5.0 # base spacing (m) - spawn_offset: 7 # multiples of safe_distance upstream - dest_offset: 7 # multiples of safe_distance downstream - spawn_z_lift: 0.3 # small lift to avoid ground collision - wp_step: 1.0 # stepping granularity along lanes (m) - allow_uturn: false - visualize: - entry_wp: false - exit_wp: false - spawn_wp: false - dest_wp: false - route_line: false - junction_center: true - bbox: false - extent_box: true - text: true - life_time: 10.0 - -agents: - debug: false - agent_type: "marl" - - marl: - # Base VanillaAgent parameters - debug: false - max_speed: 65 # Maximum speed for the agent (km/h) - emergency_param: 0.4 - ignore_traffic_light: true - collision_time_ahead: 1.5 - local_planner: - trajectory_update_freq: 5 - debug: false - debug_trajectory: false # Keep trajectory clean for RL - -MARL: - # Algorithm selection: q_learning, td3, sac, ppo, dqn - algorithm: "td3" - - state_dim: 9 # Auto-calculated from custom features (9D total) - action_dim: 1 # Continuous speed control - - # TD3 Algorithm Configuration (Multi-Agent) - td3: - # Feature configuration (9D total) - features: - rel_x: 1 # Relative X to intersection - rel_y: 1 # Relative Y to intersection - position_x: 1 # Absolute X position - position_y: 1 # Absolute Y position - lane_position: 1 # Lane position (0=intersection, 1=left, 2=middle, 3=right) - heading_angle: 1 # Vehicle orientation (radians) - dist_to_intersection: 1 # Distance to intersection - dist_to_front_vehicle: 1 # Distance to front vehicle - waypoint_buffer: 1 # Waypoint count (0-50 range) - - # LSTM conflict encoder (from ITS_Sim architecture) - conflict_encoder: - type: "LSTM" - input_size: 9 # From custom features (auto-calculated) - hidden_size: 256 # LSTM hidden dimension - num_layers: 1 # LSTM layers - - # Motion planner architecture (from AdvRAIM) - motion_planner: - num_layers: 5 - input_dim: [265, 1024, 1024, 512, 256] # First: 9 + 256 = 265 - output_dim: [1024, 1024, 512, 256, 1] # Final output: 1 (action) - - # Learning rates - reduced for stable training - learning_rate_actor: 1e-3 # Actor learning rate (stable learning) - learning_rate_critic: 1e-3 # Critic learning rate (stable convergence) - - # TD3 hyperparameters - tau: 0.01 # Soft update parameter for target networks (2x faster updates) - discount: 0.99 # Discount factor (gamma) - policy_noise: 0.2 # Noise added to target policy during critic update - noise_clip: 0.3 # Range to clip target policy noise - exploration_noise: 0.5 # Maximum noise for aggressive speed exploration (±21 km/h) - policy_freq: 2 # Delayed policy updates (update actor every 2 critic updates) - - # Training parameters - batch_size: 512 # Training batch size (large for stable gradients) - memory_size: 300000 # Replay buffer size (large for diverse experience) - min_memory_size: 25000 # Minimum transitions before training starts - save_frequency: 1000 # Save model every N episodes - eval_frequency: 500 # Evaluate policy every N episodes - - # Training configuration - training: - load_model: false # Load pre-trained model if available - model_dir: "models/td3/" # Model save directory - enable_wandb: false # Weights & Biases logging - wandb_project: "opencda_marl" # WandB project name \ No newline at end of file diff --git a/configs/marl/intersection_vanilla.yaml b/configs/marl/intersection_vanilla.yaml deleted file mode 100644 index 82592eb..0000000 --- a/configs/marl/intersection_vanilla.yaml +++ /dev/null @@ -1,57 +0,0 @@ -# Extend fields - only these fields will extend instead of replace -_extend_fields: - - "world.seed" # Add to base seed for variation - -description: |- - MARL configuration for 4-way intersection with HIGH traffic density. - This scenario focuses on intersection navigation under heavy congestion. - -# Demonstrate seed extension - adds variation to base seed -world: - seed: 5 # This will be ADDED to default.yaml seed (11) = 16 total - -spectator: - preset: "intersection_bird_eye" - -meta: - # available scenario types: intersection - scenario_type: "intersection" - town: "intersection_12_lane_fixed" - # custom xodr path for this scenario - # xodr_path: "opencda_marl/assets/maps/intersection.xodr" - -# minimal scenario configuration -scenario: - simulation: - max_steps: 2400 # 2 minute = 20fps * 60s * 2 - max_episodes: 100 - traffic: - # Traffic mode: 'record', 'replay', or 'live' - mode: "replay" - replay_file: "recordings/lite_2minL.json" - #replay_file: "recordings/quick_test.json" - base_speed: 45.0 # km/h base speed for vehicles - active_junctions: [4] - - planner: - distance: 5.0 # base spacing (m) - spawn_offset: 7 # multiples of safe_distance upstream - dest_offset: 7 # multiples of safe_distance downstream - spawn_z_lift: 0.3 # small lift to avoid ground collision - wp_step: 1.0 # stepping granularity along lanes (m) - allow_uturn: false - visualize: - entry_wp: false - exit_wp: false - spawn_wp: false - dest_wp: false - route_line: false - junction_center: true - bbox: false - extent_box: true - text: true - life_time: 10.0 - -agents: - debug: false - agent_type: "vanilla" \ No newline at end of file diff --git a/configs/marl/mappo.yaml b/configs/marl/mappo.yaml new file mode 100644 index 0000000..14e0686 --- /dev/null +++ b/configs/marl/mappo.yaml @@ -0,0 +1,172 @@ +# MAPPO (Multi-Agent Proximal Policy Optimization) Configuration +# Reference: "The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games" +# Yu et al., NeurIPS 2021 +# GitHub: https://github.com/marlbenchmark/on-policy +# +# Citation: +# @article{yu2022surprising, +# title={The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games}, +# author={Yu, Chao and Velu, Akash and Vinitsky, Eugene and others}, +# journal={Advances in Neural Information Processing Systems}, +# volume={35}, +# pages={24611--24624}, +# year={2022} +# } + +description: |- + MAPPO (Multi-Agent Proximal Policy Optimization) for cooperative autonomous + intersection management. Uses CTDE paradigm with centralized critic and + decentralized actors. Simplified 8D state with warmup for stable learning. + +_extend_fields: + - "world.seed" + - "MARL.training.metrics_export_interval" + - "MARL.training.metrics_export_dir" + +world: + seed: 5 # Added to default seed (11) = 16 total + +agents: + agent_type: "marl" + + marl: + debug: false + max_speed: 65 # km/h + emergency_param: 0.4 + ignore_traffic_light: true + collision_time_ahead: 1.5 + + local_planner: + trajectory_update_freq: 5 + debug: false + debug_trajectory: true + +MARL: + algorithm: "mappo" + + # Simplified 8D state space (no nearby vehicles for simpler learning) + state_dim: 8 + action_dim: 1 # Continuous speed control + + mappo: + # ============ Feature Configuration (8D simplified) ============ + features: + rel_x: 1 + rel_y: 1 + speed: 1 + heading_angle: 1 + dist_to_intersection: 1 + dist_to_front_vehicle: 1 + lane_position: 1 + waypoint_buffer: 1 + + # ============ Network Architecture ============ + # Actor network (decentralized - uses local observation) + actor_hidden_dims: [256, 256] + + # Critic network (centralized - sees global state) + critic_hidden_dims: [256, 256] + + # LSTM encoder for multi-agent context + lstm_hidden_size: 128 + lstm_num_layers: 1 + + # Initial exploration std (Gaussian policy) - lower for less noisy initial policy + init_std: 0.3 + + # ============ PPO Hyperparameters ============ + # Clipping parameter (prevents large policy updates) - smaller for stability + clip_param: 0.1 + + # Number of epochs per update - fewer for more conservative updates + ppo_epochs: 5 + + # Mini-batches per epoch + num_mini_batch: 4 + + # ============ Loss Coefficients ============ + # Entropy coefficient (encourages exploration) - higher for better exploration + entropy_coef: 0.05 + + # Value loss coefficient + value_loss_coef: 0.5 + + # Gradient clipping (prevents gradient explosion) + max_grad_norm: 0.5 + + # ============ GAE Parameters ============ + # Discount factor + gamma: 0.99 + + # GAE lambda (bias-variance tradeoff) + gae_lambda: 0.95 + + # Whether to use GAE (recommended) + use_gae: true + + # ============ Learning Rate ============ + learning_rate: 3e-4 + + # Optional: Linear LR decay + use_lr_decay: false + + # ============ Rollout Settings ============ + # Steps to collect before each update + n_steps: 128 + + # Mini-batch size for updates + batch_size: 32 + + # ============ Warmup Configuration ============ + # Random exploration before policy learning for better initial data + warmup_steps: 2000 + + # ============ Action Bounds ============ + max_action: 65.0 # Maximum speed in km/h + min_action: 10.0 # Minimum speed to prevent "freeze" behavior + + # ============ CTDE Settings ============ + # Centralized Training, Decentralized Execution + use_centralized_V: true # Critic sees global state + share_policy: true # All agents share actor parameters + + training: + training_mode: false + checkpoint_dir: "checkpoints/" + save_freq: 10 # Save every 10 episodes + keep_checkpoints: 3 + load_checkpoint: "checkpoints/mappo_300vph/latest_checkpoint.pth" + + # ============ Rewards ============ + rewards: + # Terminal rewards + collision: -500.0 + success: 400.0 + + # Per-step rewards + step_penalty: -0.8 + speed_bonus: 0.3 + speed_threshold: 45.0 + + # TTC-based safety reward (DISABLED) + ttc_safe_threshold: 4.0 + ttc_max_penalty: 0.0 + ttc_decay_rate: 1.5 + ttc_near_miss_threshold: 2.0 + + # Clearance speed bonus + clearance_speed_bonus: 0.3 + clearance_threshold: 30.0 + + # Progress reward + progress_scale: 0.4 + junction_threshold: 5.0 + + # Stop penalty + stop_threshold: 3.0 + stop_penalty: -1.5 + + # Yielding bonus + yielding_bonus: 0.5 + yielding_ttc_threshold: 3.0 + yielding_speed_drop: 5.0 diff --git a/configs/marl/intersection_qbalanced.yaml b/configs/marl/qbalanced.yaml similarity index 100% rename from configs/marl/intersection_qbalanced.yaml rename to configs/marl/qbalanced.yaml diff --git a/configs/marl/rule_based.yaml b/configs/marl/rule_based.yaml new file mode 100644 index 0000000..b6293bf --- /dev/null +++ b/configs/marl/rule_based.yaml @@ -0,0 +1,41 @@ +description: |- + Rule-based 3-stage agent for intersection scenario. + Traditional agent with JAIR enhancements - no training required. + +agents: + agent_type: "rule_based" + + rule_based: + debug: false + debug_rules: false + max_speed: 65 # km/h + emergency_param: 0.4 + ignore_traffic_light: true + collision_time_ahead: 1.5 + + local_planner: + trajectory_update_freq: 5 + debug: false + debug_trajectory: true + + # Stage 1: Junction Management + junction_approach_distance: 70.0 # meters + junction_conflict_distance: 50.0 # meters + junction_speed_factor: 0.6 + junction_min_reduction: 30.0 # km/h + + # Trajectory-based conflict detection + trajectory_lookahead_time: 8.0 # seconds + ttc_threshold: 5.0 # seconds + trajectory_step_size: 3.0 # meters + + # Stage 2: Car Following + following_speed_buffer: 8.0 # km/h + time_headway: 3.0 # seconds + following_gain: 0.5 + minimum_distance_buffer: 5.0 # meters + same_lane_tolerance_deg: 30 + front_cone_angle_deg: 45 + + # Stage 3: Cruising + cruising_speed_factor: 1.0 diff --git a/configs/marl/sac.yaml b/configs/marl/sac.yaml new file mode 100644 index 0000000..1a10e18 --- /dev/null +++ b/configs/marl/sac.yaml @@ -0,0 +1,157 @@ +# SAC (Soft Actor-Critic) Configuration +# Reference: "Soft Actor-Critic: Off-Policy Maximum Entropy Deep Reinforcement Learning +# with a Stochastic Actor" - Haarnoja et al., ICML 2018 (arXiv:1801.01290) +# "Soft Actor-Critic Algorithms and Applications" - Haarnoja et al., 2018 +# (arXiv:1812.05905) +# +# Citation: +# @inproceedings{haarnoja2018soft, +# title={Soft Actor-Critic: Off-Policy Maximum Entropy Deep Reinforcement Learning +# with a Stochastic Actor}, +# author={Haarnoja, Tuomas and Zhou, Aurick and Abbeel, Pieter and Levine, Sergey}, +# booktitle={International Conference on Machine Learning}, +# pages={1861--1870}, +# year={2018} +# } + +description: |- + SAC (Soft Actor-Critic) for autonomous intersection management. + Uses maximum entropy framework with auto-tuning temperature for + balanced exploration-exploitation. Adapts exploration based on + situational context (e.g., nearby vehicles require different speeds). + +_extend_fields: + - "world.seed" + - "MARL.training.metrics_export_interval" + - "MARL.training.metrics_export_dir" + +world: + seed: 5 # Added to default seed (11) = 16 total + +agents: + agent_type: "marl" + + marl: + debug: false + max_speed: 65 # km/h + emergency_param: 0.4 + ignore_traffic_light: true + collision_time_ahead: 1.5 + + local_planner: + trajectory_update_freq: 5 + debug: false + debug_trajectory: true + +MARL: + algorithm: "sac" + + # Simplified 8D state space + state_dim: 8 + action_dim: 1 # Continuous speed control + + sac: + # ============ Feature Configuration (8D) ============ + features: + rel_x: 1 + rel_y: 1 + speed: 1 + heading_angle: 1 + dist_to_intersection: 1 + dist_to_front_vehicle: 1 + lane_position: 1 + waypoint_buffer: 1 + + # ============ Network Architecture ============ + # LSTM encoder for multi-agent context + lstm_hidden_size: 128 + lstm_num_layers: 1 + + # Actor network (squashed Gaussian policy) + actor_hidden_dims: [256, 256] + + # Critic network (twin Q-networks) + critic_hidden_dims: [256, 256] + + # ============ Learning Rates ============ + learning_rate_actor: 3e-4 + learning_rate_critic: 3e-4 + learning_rate_alpha: 3e-4 # For auto-tuning temperature + + # ============ SAC Hyperparameters ============ + # Soft update coefficient for target networks + tau: 0.005 + + # Discount factor + gamma: 0.99 + + # ============ Entropy Tuning ============ + # Auto-tune alpha (temperature) - KEY SAC FEATURE + # This adapts exploration based on the situation: + # - High alpha = more exploration (when uncertain) + # - Low alpha = more exploitation (when confident) + auto_entropy_tuning: true + + # Target entropy = -action_dim (standard choice) + # This encourages the policy to maintain a certain level of randomness + target_entropy: -1.0 + + # Initial temperature value + init_alpha: 0.2 + + # ============ Gaussian Policy Bounds ============ + # Clamp log_std for numerical stability + log_std_min: -20 + log_std_max: 2 + + # ============ Replay Buffer ============ + memory_size: 25000 + batch_size: 256 + + # Warmup: random exploration before learning + warmup_steps: 1000 + + # ============ Action Bounds ============ + max_action: 65.0 # Maximum speed in km/h + min_action: 0.0 # Allow full stops for yielding + + training: + training_mode: false + checkpoint_dir: "checkpoints/" + save_freq: 5 + keep_checkpoints: 3 + load_checkpoint: "checkpoints/sac_300vph/latest_checkpoint.pth" + + # ============ Rewards ============ + rewards: + # Terminal rewards + collision: -500.0 + success: 400.0 + + # Per-step rewards + step_penalty: -0.8 + speed_bonus: 0.3 + speed_threshold: 45.0 + + # TTC-based safety reward (DISABLED) + ttc_safe_threshold: 4.0 + ttc_max_penalty: 0.0 + ttc_decay_rate: 1.5 + ttc_near_miss_threshold: 2.0 + + # Clearance speed bonus + clearance_speed_bonus: 0.3 + clearance_threshold: 30.0 + + # Progress reward + progress_scale: 0.4 + junction_threshold: 5.0 + + # Stop penalty + stop_threshold: 3.0 + stop_penalty: -1.5 + + # Yielding bonus + yielding_bonus: 0.5 + yielding_ttc_threshold: 3.0 + yielding_speed_drop: 5.0 diff --git a/configs/marl/sumo.yaml b/configs/marl/sumo.yaml new file mode 100644 index 0000000..1327796 --- /dev/null +++ b/configs/marl/sumo.yaml @@ -0,0 +1,113 @@ +description: |- + SUMO-only MARL training for intersection navigation. + Uses TraCI for 10-80x faster training compared to CARLA. + Trained policies can be transferred to CARLA for fine-tuning. + +meta: + scenario_type: "intersection_sumo" + simulator: "sumo" + sumo_cfg: "opencda_marl/assets/intersection_sumo/intersection.sumocfg" + +world: + sync_mode: true + fixed_delta_seconds: 0.05 + seed: 11 + sumo_port: 8873 + sumo_gui: true # Set to false for headless training + +scenario: + simulation: + max_steps: 2400 + max_episodes: 1000 # Fast training in SUMO + + traffic: + mode: "replay" + replay_file: "recordings/lite_2min_SUMO.json" + base_speed: 45.0 + active_junctions: [4] + + planner: + distance: 5.0 + spawn_offset: 7 + dest_offset: 7 + spawn_z_lift: 0.3 + wp_step: 1.0 + allow_uturn: false + +agents: + count: 10 # Can scale to 50+ in SUMO + agent_type: "marl" + + marl: + max_speed: 65 + emergency_param: 0.4 + ignore_traffic_light: true + collision_time_ahead: 1.5 + +MARL: + algorithm: "td3" + + state_dim: 9 + action_dim: 1 + + td3: + features: + rel_x: 1 + rel_y: 1 + position_x: 1 + position_y: 1 + lane_position: 1 + heading_angle: 1 + dist_to_intersection: 1 + dist_to_front_vehicle: 1 + waypoint_buffer: 1 + + conflict_encoder: + type: "LSTM" + input_size: 9 + hidden_size: 256 + num_layers: 1 + + motion_planner: + num_layers: 5 + input_dim: [265, 1024, 1024, 512, 256] + output_dim: [1024, 1024, 512, 256, 1] + + learning_rate_actor: 1e-3 + learning_rate_critic: 1e-3 + + tau: 0.01 + discount: 0.99 + policy_noise: 0.2 + noise_clip: 0.3 + exploration_noise: 0.5 # Higher exploration in SUMO + policy_freq: 2 + + batch_size: 256 + memory_size: 25000 + recency_ratio: 0.5 + clear_episodes: 1 + clear_keep_ratio: 0.6 + + use_per: true + per_alpha: 0.6 + per_beta: 0.4 + per_beta_increment: 0.001 + + max_action: 65.0 + min_action: 30.0 + warmup_steps: 500 + + training: + training_mode: true + checkpoint_dir: "checkpoints/sumo_td3/" + save_freq: 10 + keep_checkpoints: 5 + load_checkpoint: null + + rewards: + collision: -500.0 + success: 400.0 + step_penalty: -1.5 + speed_bonus: 0.5 + speed_threshold: 45.0 diff --git a/configs/marl/td3_43d.yaml b/configs/marl/td3_43d.yaml new file mode 100644 index 0000000..8f86827 --- /dev/null +++ b/configs/marl/td3_43d.yaml @@ -0,0 +1,147 @@ +description: |- + TD3 with corrected 43D state space (was incorrectly labeled as 44D). + Removed redundant position_x/position_y since we already have rel_x/rel_y. + 7D ego + 35D nearby vehicles + 1D waypoint_buffer = 43D total. + +_extend_fields: + - "world.seed" + - "MARL.training.metrics_export_interval" + - "MARL.training.metrics_export_dir" + +world: + seed: 5 # Added to default seed (11) = 16 total + +agents: + agent_type: "marl" + + marl: + debug: false + max_speed: 65 # km/h + emergency_param: 0.4 + ignore_traffic_light: true + collision_time_ahead: 1.5 + + local_planner: + trajectory_update_freq: 5 + debug: false + debug_trajectory: true + +MARL: + algorithm: "td3" + + # Corrected state/action dimensions + state_dim: 43 # 8D ego + 35D nearby vehicle features + action_dim: 1 # Continuous speed control + + td3: + # Feature configuration (43D total) + # Removed position_x and position_y as they're redundant with rel_x/rel_y + features: + rel_x: 1 + rel_y: 1 + # position_x: 1 + # position_y: 1 + lane_position: 1 + heading_angle: 1 + dist_to_intersection: 1 + dist_to_front_vehicle: 1 + waypoint_buffer: 1 + speed: 1 + nearby_vehicles: 35 + + # Nearby vehicle detection + nearby_vehicle_config: + detection_radius: 30.0 + max_vehicles: 5 + features_per_vehicle: 7 + include_ttc: true + max_relative_velocity: 40.0 + max_ttc: 10.0 + + # LSTM conflict encoder + conflict_encoder: + type: "LSTM" + input_size: 43 + hidden_size: 256 + num_layers: 1 + + # Motion planner network architecture + motion_planner: + num_layers: 3 + input_dim: [299, 512, 256] + output_dim: [512, 256, 1] + + # Learning rates + learning_rate_actor: 1e-4 + learning_rate_critic: 1e-3 + + # TD3 hyperparameters + tau: 0.005 + discount: 0.99 + policy_noise: 0.2 + noise_clip: 0.5 + + # Smart distance-aware exploration + exploration_noise: 0.3 + noise_decay: 0.998 + min_exploration_noise: 0.05 + policy_freq: 2 + + # Training parameters + batch_size: 256 + + # Prioritized Experience Replay (PER) + memory_size: 100000 + recency_ratio: 0.5 + clear_episodes: 10 + clear_keep_ratio: 0.9 + use_per: true + per_alpha: 0.6 + per_beta: 0.4 + per_beta_increment: 1.0e-4 + per_epsilon: 1.0e-6 + + # Action bounds (km/h) + max_action: 65.0 + min_action: 10.0 + warmup_steps: 0 + + training: + training_mode: false + checkpoint_dir: "checkpoints/" + save_freq: 5 + keep_checkpoints: 3 + load_checkpoint: "checkpoints/td3_43d_300vph/latest_checkpoint.pth" + + rewards: + # Terminal rewards + collision: -500.0 + success: 400.0 + + # Per-step rewards + step_penalty: -0.8 + speed_bonus: 0.3 + speed_threshold: 45.0 + + # TTC-based safety reward + ttc_safe_threshold: 4.0 + ttc_max_penalty: -15.0 + ttc_decay_rate: 1.5 + ttc_near_miss_threshold: 2.0 + + # Clearance speed bonus + clearance_speed_bonus: 0.3 + clearance_threshold: 30.0 + + # Progress reward + progress_scale: 0.4 + junction_threshold: 5.0 + + # Stop penalty + stop_threshold: 3.0 + stop_penalty: -1.5 + + # Yielding bonus + yielding_bonus: 0.5 + yielding_ttc_threshold: 3.0 + yielding_speed_drop: 5.0 diff --git a/configs/marl/td3_44d.yaml b/configs/marl/td3_44d.yaml new file mode 100644 index 0000000..17c46c6 --- /dev/null +++ b/configs/marl/td3_44d.yaml @@ -0,0 +1,149 @@ +description: |- + TD3 (Twin Delayed DDPG) deep reinforcement learning agent. + Continuous action space with LSTM conflict encoder. + +_extend_fields: + - "world.seed" + - "MARL.training.metrics_export_interval" + - "MARL.training.metrics_export_dir" + +world: + seed: 5 # Added to default seed (11) = 16 total + +agents: + agent_type: "marl" + + marl: + debug: false + max_speed: 65 # km/h + emergency_param: 0.4 + ignore_traffic_light: true + collision_time_ahead: 1.5 + + local_planner: + trajectory_update_freq: 5 + debug: false + debug_trajectory: true + +MARL: + algorithm: "td3" + + # State/Action dimensions + state_dim: 44 # 9D ego + 35D nearby vehicles + action_dim: 1 # Continuous speed control + + td3: + # Feature configuration (44D total) + features: + rel_x: 1 + rel_y: 1 + position_x: 1 + position_y: 1 + lane_position: 1 + heading_angle: 1 + dist_to_intersection: 1 + dist_to_front_vehicle: 1 + waypoint_buffer: 1 + nearby_vehicles: 35 # 5 vehicles x 7 features + + # Nearby vehicle detection + nearby_vehicle_config: + detection_radius: 30.0 # Reduced from 50m - focus on immediate threats + max_vehicles: 5 + features_per_vehicle: 7 + include_ttc: true + max_relative_velocity: 40.0 + max_ttc: 10.0 + + # LSTM conflict encoder + conflict_encoder: + type: "LSTM" + input_size: 44 + hidden_size: 256 + num_layers: 1 + + # Motion planner architecture + motion_planner: + num_layers: 5 + input_dim: [300, 1024, 1024, 512, 256] + output_dim: [1024, 1024, 512, 256, 1] + + # Learning rates + learning_rate_actor: 1e-3 + learning_rate_critic: 1e-3 + + # TD3 hyperparameters + tau: 0.01 + discount: 0.99 + policy_noise: 0.2 + noise_clip: 0.3 + # Position-aware exploration: base noise scaled by distance to intersection + exploration_noise: 0.5 # Higher initial (was 0.3) + noise_decay: 0.998 # Slower decay (was 0.995) + min_exploration_noise: 0.15 # Higher floor (was 0.05) + policy_freq: 2 + + # Training parameters + batch_size: 256 + + # Replay buffer + memory_size: 50000 # Increased from 25000 - more diverse experiences + recency_ratio: 0.5 + clear_episodes: 1 + clear_keep_ratio: 0.6 + + # Prioritized Experience Replay + use_per: true + per_alpha: 0.6 + per_beta: 0.4 + per_beta_increment: 0.001 + + # Action bounds (km/h) + max_action: 65.0 + min_action: 0.0 # Allow full stops for yielding (was 10.0) + warmup_steps: 0 + + training: + training_mode: false + checkpoint_dir: "checkpoints/" + save_freq: 1 + keep_checkpoints: 3 + load_checkpoint: "checkpoints/td3_44d_400vph/latest_checkpoint.pth" + + rewards: + # Terminal rewards + collision: -500.0 + success: 400.0 + + # Per-step rewards + # Reduced step_penalty to avoid incentivizing unsafe speeding + step_penalty: -0.8 + speed_bonus: 0.3 + speed_threshold: 45.0 # Lower threshold for speed bonus + + # TTC-based safety reward - DISABLED (collision penalty is sufficient) + # TTC penalty was causing overly conservative driving behavior + # Keep these params for metrics tracking only + ttc_safe_threshold: 4.0 # seconds - for metrics tracking + ttc_max_penalty: 0.0 # DISABLED - set to 0 (was 10.0) + ttc_decay_rate: 1.5 # kept for reference + ttc_near_miss_threshold: 2.0 # TTC below this counts as near-miss for metrics + + # Clearance speed bonus - NEW: reward for going fast when path is clear + # This teaches: "go fast when safe, collision penalty teaches when to slow" + clearance_speed_bonus: 0.3 # max bonus when at max speed with clear path + clearance_threshold: 30.0 # distance (m) to consider path "clear" + + # Progress reward - slightly reduced to balance with safety + progress_scale: 0.4 + junction_threshold: 5.0 + + # Stop penalty - reduced to not overly punish cautious stopping + stop_threshold: 3.0 # km/h - more lenient threshold + stop_penalty: -1.5 # reduced penalty (was -3.0) + + # Yielding bonus - reward for slowing when nearby vehicle has low TTC + # Encourages cooperative behavior: slow down to let others pass safely + yielding_bonus: 0.5 # bonus when yielding successfully + yielding_ttc_threshold: 3.0 # TTC below this triggers yielding check + yielding_speed_drop: 5.0 # speed reduction (km/h) to qualify as yielding (reduced from 10.0) diff --git a/configs/marl/td3_simple.yaml b/configs/marl/td3_simple.yaml new file mode 100644 index 0000000..47bd07b --- /dev/null +++ b/configs/marl/td3_simple.yaml @@ -0,0 +1,129 @@ +description: |- + TD3 Simple - Lightweight TD3 with 8D state space for comparison testing. + Uses existing TD3 algorithm with simplified state features (no nearby vehicles). + +_extend_fields: + - "world.seed" + - "MARL.training.metrics_export_interval" + - "MARL.training.metrics_export_dir" + +world: + seed: 5 # Added to default seed (11) = 16 total + +agents: + agent_type: "marl" + + marl: + debug: false + max_speed: 65 # km/h + emergency_param: 0.4 + ignore_traffic_light: true + collision_time_ahead: 1.5 + + local_planner: + trajectory_update_freq: 5 + debug: false + debug_trajectory: true + +MARL: + algorithm: "td3" + + # Simplified 8D state space (no nearby vehicles) + state_dim: 8 + action_dim: 1 # Continuous speed control + + td3: + # 8D Feature configuration (simplified - no nearby_vehicles) + features: + rel_x: 1 + rel_y: 1 + speed: 1 + heading_angle: 1 + dist_to_intersection: 1 + dist_to_front_vehicle: 1 + lane_position: 1 + waypoint_buffer: 1 + + # LSTM conflict encoder (smaller for 8D input) + conflict_encoder: + type: "LSTM" + input_size: 8 + hidden_size: 128 # Reduced from 256 + num_layers: 1 + + # Simplified motion planner architecture + motion_planner: + num_layers: 3 + input_dim: [136, 256, 128] # 8 + 128 = 136D input + output_dim: [256, 128, 1] + + # Learning rates - lower for stability + learning_rate_actor: 5e-5 + learning_rate_critic: 5e-4 + + # TD3 hyperparameters + tau: 0.005 + discount: 0.99 + policy_noise: 0.2 + noise_clip: 0.5 + + # Simplified exploration (no distance-aware noise) + exploration_noise: 0.3 + noise_decay: 0.999 # Slower decay for longer exploration + min_exploration_noise: 0.05 + policy_freq: 2 + + # Training parameters + batch_size: 128 # Smaller batch + + # Replay buffer (no PER for simplicity) + memory_size: 50000 # Larger buffer for stability + recency_ratio: 0.5 + clear_episodes: 5 # Less aggressive clearing + clear_keep_ratio: 0.8 # Keep more experiences + use_per: false # Disable PER for simplicity + + # Action bounds (km/h) + max_action: 65.0 + min_action: 10.0 # Minimum speed to prevent freeze behavior + warmup_steps: 0 # No warmup - TD3 learns well without it + + training: + training_mode: false + checkpoint_dir: "checkpoints/" + save_freq: 5 + keep_checkpoints: 3 + load_checkpoint: "checkpoints/td3_8d_300vph/latest_checkpoint.pth" + + rewards: + # Terminal rewards + collision: -500.0 + success: 400.0 + + # Per-step rewards + step_penalty: -0.8 + speed_bonus: 0.3 + speed_threshold: 45.0 + + # TTC-based safety reward (DISABLED) + ttc_safe_threshold: 4.0 + ttc_max_penalty: 0.0 + ttc_decay_rate: 1.5 + ttc_near_miss_threshold: 2.0 + + # Clearance speed bonus + clearance_speed_bonus: 0.3 + clearance_threshold: 30.0 + + # Progress reward + progress_scale: 0.4 + junction_threshold: 5.0 + + # Stop penalty + stop_threshold: 3.0 + stop_penalty: -1.5 + + # Yielding bonus + yielding_bonus: 0.5 + yielding_ttc_threshold: 3.0 + yielding_speed_drop: 5.0 diff --git a/configs/marl/td3_simple_v3.yaml b/configs/marl/td3_simple_v3.yaml new file mode 100644 index 0000000..c7bbbc1 --- /dev/null +++ b/configs/marl/td3_simple_v3.yaml @@ -0,0 +1,138 @@ +description: |- + TD3 Simple V3 - Improved lightweight TD3 inspired by successful SAC/DQN 8D configurations. + Key improvements over td3_simple: + - Conservative exploration with gradual noise decay (like SAC) + - Lower learning rates for stability + - Smaller LSTM hidden size to prevent overfitting on 8D input + - Increased batch size for more stable gradients + - Enhanced reward shaping with cooperative yielding + +_extend_fields: + - "world.seed" + - "MARL.training.metrics_export_interval" + - "MARL.training.metrics_export_dir" + +world: + seed: 5 # Added to default seed (11) = 16 total + +agents: + agent_type: "marl" + + marl: + debug: false + max_speed: 65 # km/h + emergency_param: 0.4 + ignore_traffic_light: true + collision_time_ahead: 1.5 + + local_planner: + trajectory_update_freq: 5 + debug: false + debug_trajectory: true + +MARL: + algorithm: "td3" + + # Simplified 8D state space (no nearby vehicles) + state_dim: 8 + action_dim: 1 # Continuous speed control + + td3: + # 8D Feature configuration (simplified - no nearby_vehicles) + features: + rel_x: 1 + rel_y: 1 + speed: 1 + heading_angle: 1 + dist_to_intersection: 1 + dist_to_front_vehicle: 1 + lane_position: 1 + waypoint_buffer: 1 + + # LSTM conflict encoder (smaller to match 8D input) + conflict_encoder: + type: "LSTM" + input_size: 8 + hidden_size: 256 + num_layers: 1 + + # Simplified motion planner architecture + motion_planner: + num_layers: 3 + input_dim: [264, 512, 256] + output_dim: [512, 256, 1] + + # Learning rates + learning_rate_actor: 1e-4 + learning_rate_critic: 1e-3 + + # TD3 hyperparameters + tau: 0.005 + discount: 0.99 + policy_noise: 0.2 + noise_clip: 0.5 + + # Conservative exploration (inspired by SAC's success) + exploration_noise: 0.3 + noise_decay: 0.998 + min_exploration_noise: 0.05 + policy_freq: 2 + + # Training parameters + batch_size: 256 + + # Replay buffer with PER for sample efficiency + memory_size: 100000 + recency_ratio: 0.5 + clear_episodes: 10 + clear_keep_ratio: 0.9 + use_per: true + per_alpha: 0.6 + per_beta: 0.4 + per_beta_increment: 1.0e-4 + per_epsilon: 1.0e-6 + + # Action bounds (km/h) + max_action: 65.0 + min_action: 10.0 + warmup_steps: 1000 + + training: + training_mode: false + checkpoint_dir: "checkpoints/" + save_freq: 5 + keep_checkpoints: 3 + load_checkpoint: "checkpoints/td3_8d_300vph_v3/latest_checkpoint.pth" + + rewards: + # Terminal rewards + collision: -500.0 + success: 400.0 + + # Per-step rewards + step_penalty: -0.8 + speed_bonus: 0.3 + speed_threshold: 45.0 + + # TTC-based safety reward (DISABLED for 8D baseline) + ttc_safe_threshold: 4.0 + ttc_max_penalty: 0.0 + ttc_decay_rate: 1.5 + ttc_near_miss_threshold: 2.0 + + # Clearance speed bonus + clearance_speed_bonus: 0.3 + clearance_threshold: 30.0 + + # Progress reward + progress_scale: 0.4 + junction_threshold: 5.0 + + # Stop penalty + stop_threshold: 3.0 + stop_penalty: -1.5 + + # Yielding bonus + yielding_bonus: 0.5 + yielding_ttc_threshold: 3.0 + yielding_speed_drop: 5.0 diff --git a/configs/marl/td3_simple_v4.yaml b/configs/marl/td3_simple_v4.yaml new file mode 100644 index 0000000..033d6ae --- /dev/null +++ b/configs/marl/td3_simple_v4.yaml @@ -0,0 +1,132 @@ +description: |- + TD3 Simple V4 - Addresses slow speed issue in V3 by rebalancing rewards. + Key changes: + - Stronger speed incentives (higher bonus, lower threshold) + - Reduced step penalty to encourage movement + - Velocity-proportional progress reward + - Maintained large network architecture + +_extend_fields: + - "world.seed" + - "MARL.training.metrics_export_interval" + - "MARL.training.metrics_export_dir" + +world: + seed: 5 + +agents: + agent_type: "marl" + + marl: + debug: false + max_speed: 65 + emergency_param: 0.4 + ignore_traffic_light: true + collision_time_ahead: 1.5 + + local_planner: + trajectory_update_freq: 5 + debug: false + debug_trajectory: true + +MARL: + algorithm: "td3" + + state_dim: 8 + action_dim: 1 + + td3: + # 8D features (same as v3) + features: + rel_x: 1 + rel_y: 1 + speed: 1 + heading_angle: 1 + dist_to_intersection: 1 + dist_to_front_vehicle: 1 + lane_position: 1 + waypoint_buffer: 1 + + # Network architecture (keep v3's larger network) + conflict_encoder: + type: "LSTM" + input_size: 8 + hidden_size: 256 + num_layers: 1 + + motion_planner: + num_layers: 3 + input_dim: [264, 512, 256] # 8 + 256 = 264 + output_dim: [512, 256, 1] + + # Learning rates (keep v3's rates) + learning_rate_actor: 1e-4 + learning_rate_critic: 1e-3 + + # TD3 hyperparameters + tau: 0.005 + discount: 0.99 + policy_noise: 0.2 + noise_clip: 0.5 + + # Exploration (keep v3's settings) + exploration_noise: 0.3 + noise_decay: 0.998 + min_exploration_noise: 0.05 + policy_freq: 2 + + # Training parameters + batch_size: 256 + memory_size: 100000 + recency_ratio: 0.5 + clear_episodes: 10 + clear_keep_ratio: 0.9 + use_per: true + per_alpha: 0.6 + per_beta: 0.4 + per_beta_increment: 1.0e-4 + per_epsilon: 1.0e-6 + + max_action: 65.0 + min_action: 10.0 + warmup_steps: 1000 + + training: + training_mode: true + checkpoint_dir: "checkpoints/" + save_freq: 5 + keep_checkpoints: 3 + load_checkpoint: "" + + rewards: + # Terminal rewards + collision: -500.0 + success: 400.0 + + # Per-step rewards - REBALANCED FOR SPEED + step_penalty: -0.5 + speed_bonus: 1.0 + speed_threshold: 35.0 + + # TTC-based safety (disabled) + ttc_safe_threshold: 4.0 + ttc_max_penalty: 0.0 + ttc_decay_rate: 1.5 + ttc_near_miss_threshold: 2.0 + + # Clearance speed bonus + clearance_speed_bonus: 1.0 + clearance_threshold: 30.0 + + # Progress reward + progress_scale: 1.0 + junction_threshold: 5.0 + + # Stop penalty + stop_threshold: 3.0 + stop_penalty: -3.0 + + # Yielding bonus + yielding_bonus: 1.0 + yielding_ttc_threshold: 3.0 + yielding_speed_drop: 5.0 diff --git a/configs/marl/vanilla.yaml b/configs/marl/vanilla.yaml new file mode 100644 index 0000000..80f7faf --- /dev/null +++ b/configs/marl/vanilla.yaml @@ -0,0 +1,18 @@ +description: |- + Vanilla agent baseline for intersection scenario. + Traditional agent - no training required. + +agents: + agent_type: "vanilla" + + vanilla: + debug: false + max_speed: 45 # km/h + emergency_param: 0.4 + ignore_traffic_light: true + collision_time_ahead: 1.5 + + local_planner: + trajectory_update_freq: 9 + debug: false + debug_trajectory: true diff --git a/docs/about.md b/docs/about.md index 458999a..a9ca69c 100644 --- a/docs/about.md +++ b/docs/about.md @@ -1,37 +1,73 @@ # About Us -OpenCDA-MARL is developed by [UA Radar Lab](https://github.com/radar-lab). +OpenCDA-MARL is developed by the [UA Radar Lab](https://radar.ece.arizona.edu/) at the University of Arizona, advancing research in cooperative autonomous driving through multi-agent reinforcement learning. -## Supervisor +--- -Dr. Siyang Cao (Associate Professor @ UA) +## :material-account-supervisor: Supervisors -- Supervisor -- [UArizona ECE](https://ece.engineering.arizona.edu/faculty-staff/faculty/siyang-cao) -- [Google Scholar](https://scholar.google.com/citations?user=dQvEspMAAAAJ&hl=en) +
-Dr. Liu Bo (Associate Professor @ UA): +- **Dr. Siyang Cao** -- Co-Supervisor -- [UArizona ECE](https://ece.engineering.arizona.edu/faculty-staff/faculty/bo-liu) + --- -## Core Developer + Associate Professor, University of Arizona -Lihao Guo (Ph.D. Student @ UA): + :material-school: Supervisor -- Project Lead -- OpenCDA-MARL System Architect -- MARL Algorithm Development -- [Homepage](https://www.lgcyaxi.net/) -- [Linkedin](https://www.linkedin.com/in/lgcyaxi) + [:material-web: UArizona ECE](https://ece.engineering.arizona.edu/faculty-staff/faculty/siyang-cao)   [:material-google: Google Scholar](https://scholar.google.com/citations?user=dQvEspMAAAAJ&hl=en) -Jiahao Tang (Ph.D. Student @ UA): +- **Dr. Bo Liu** -- Rule-based Agent Configuration -- [Linkedin](https://www.linkedin.com/in/jiahaotang1997/) + --- -Louis Liu (Volunteer Student @ University High School): + Associate Professor, University of Arizona -- Map Development -- Simulation Testing -- [Linkedin](https://www.linkedin.com/in/louisliu2/) + :material-school: Co-Supervisor + + [:material-web: UArizona ECE](https://ece.engineering.arizona.edu/faculty-staff/faculty/bo-liu) + +
+ +## :material-code-braces: Core Developers + +
+ +- **Lihao Guo**   :material-star:{ .lg } + + --- + + Ph.D. Student, University of Arizona + + :material-briefcase: Project Lead · System Architect · MARL Algorithm Implementation & Analysis + + [:material-home: Homepage](https://www.lgcyaxi.net/)   [:material-linkedin: LinkedIn](https://www.linkedin.com/in/lgcyaxi) + +- **Jiahao Tang** + + --- + + Ph.D. Student, University of Arizona + + :material-briefcase: Rule-based Agent Configuration & Testing · MARL Algorithm Training + + [:material-linkedin: LinkedIn](https://www.linkedin.com/in/jiahaotang1997/) + +- **Louis Liu** + + --- + + Volunteer Student, University High School + + :material-briefcase: Map Development & Testing · Simulation Testing + + [:material-linkedin: LinkedIn](https://www.linkedin.com/in/louisliu2/) + +
+ +## :material-help-circle: Contributing + +We welcome contributions from the community. See our [Contributing Guide](contributing.md) to get started. + +[:material-github: GitHub Repository](https://github.com/radar-lab/opencda-marl){ .md-button }   [:material-bug: Report Issues](https://github.com/radar-lab/opencda-marl/issues){ .md-button .md-button--primary } diff --git a/docs/api/opencda-marl/adapters/map_adapter.md b/docs/api/opencda-marl/adapters/map_adapter.md deleted file mode 100644 index c84ff67..0000000 --- a/docs/api/opencda-marl/adapters/map_adapter.md +++ /dev/null @@ -1,433 +0,0 @@ -# Map Adapter API - -The Map Adapter provides seamless integration between OpenCDA's single-vehicle map system and MARL's multi-agent coordination requirements. It enables the best of both worlds: OpenCDA's proven map loading capabilities with MARL-specific spawn coordination and route planning. - -!!! info "Implementation Status" - The Map Adapter is **fully implemented** and provides the recommended approach for map management in MARL scenarios, bridging OpenCDA compatibility with multi-agent coordination. - -The Map Adapter follows a bridge pattern that preserves OpenCDA functionality while adding MARL capabilities: - -```text -MARLMapAdapter -├── OpenCDA Integration # Uses OpenCDA's proven map loading -│ ├── Map Loading # load_customized_world() for XODR files -│ ├── MapManager # Per-vehicle OpenCDA MapManagers -│ └── Spawn Points # Standard CARLA spawn points -└── MARL Enhancement # Adds multi-agent coordination - ├── MARLMapManager # Junction-based spawn coordination - ├── Route Planning # Spawn-destination pairs - └── Visualization # Debug tools for spawn points -``` - -```mermaid -graph TD - A[MARLMapAdapter] --> B[OpenCDA Integration] - A --> C[MARL Enhancement] - - B --> D[CARLA Map Loading] - B --> E[Standard Spawn Points] - B --> F[Per-Vehicle MapManagers] - - C --> G[Junction Analysis] - C --> H[Coordinated Spawning] - C --> I[Route Planning] - C --> J[Visualization Tools] - - G --> K[MARLMapManager] - H --> K - I --> K - J --> K -``` - -## Core Classes - -=== "MARLMapAdapter" - - The main adapter class that combines OpenCDA map loading with MARL spawn coordination. - - ```python - class MARLMapAdapter: - """ - Adapter that combines OpenCDA map loading with MARL spawn coordination. - - Architecture: - - Uses OpenCDA for map loading and basic map operations - - Uses MARLMapManager for multi-agent spawn/destination coordination - - Provides unified interface for MARL scenarios - - Design Philosophy: - - Preserve OpenCDA compatibility for single-vehicle operations - - Add MARL-specific functionality without breaking existing code - - Enable gradual migration and hybrid scenarios - """ - ``` - -=== "Constructor" - - ```python - def __init__(self, config: DictConfig, client: carla.Client): - """ - Initialize map adapter. - - Parameters - ---------- - config : DictConfig - Configuration containing map and scenario settings - client : carla.Client - CARLA client instance - """ - ``` - -#### Key Methods - -=== "MARL Spawn Coordination" - - ```python - def get_marl_spawn_points(self, num_agents: int, strategy: str = 'balanced') -> List[Dict[str, Any]]: - """ - Get spawn points optimized for MARL scenarios. - - Parameters - ---------- - num_agents : int - Number of agents to spawn - strategy : str - Spawn strategy ('balanced', 'conflict', 'random') - - Returns - ------- - spawn_info : list - List of spawn information dicts with 'transform', 'dest', and 'id' - """ - if strategy == 'balanced': - # Use MARL manager for balanced spawning across intersection arms - spawn_points = self.marl_map_manager.get_spawn_points( - num=num_agents, dest=True, detail=True - ) - elif strategy == 'conflict': - # Get spawn points that create interesting coordination challenges - all_spawns = self.marl_map_manager.get_spawn_points(detail=True) - # Select spawns that maximize potential conflicts/interactions - spawn_points = all_spawns[:num_agents] if len(all_spawns) >= num_agents else all_spawns - else: # random - spawn_points = self.marl_map_manager.get_random_spawn_points( - num=num_agents, detail=True - ) - - return spawn_points - ``` - -=== "OpenCDA Compatibility" - - ```python - def get_opencda_spawn_points(self, num_agents: int = None) -> List[carla.Transform]: - """ - Get spawn points using OpenCDA's standard approach. - - Parameters - ---------- - num_agents : int, optional - Number of spawn points to return - - Returns - ------- - spawn_points : list - List of carla.Transform objects (standard OpenCDA format) - """ - # Use CARLA's built-in spawn points (OpenCDA's standard approach) - spawn_points = self.carla_map.get_spawn_points() - - if num_agents is not None: - spawn_points = spawn_points[:num_agents] - - return spawn_points - - def create_opencda_map_manager(self, vehicle: carla.Vehicle, config: Dict) -> MapManager: - """ - Create OpenCDA MapManager for a specific vehicle. - - Parameters - ---------- - vehicle : carla.Vehicle - Vehicle to create map manager for - config : dict - Map manager configuration - - Returns - ------- - map_manager : MapManager - OpenCDA MapManager instance for the vehicle - """ - # Create standard OpenCDA MapManager for individual vehicle - map_manager = MapManager(vehicle, self.carla_map, config) - - # Store reference for cleanup - self.opencda_map_managers[vehicle.id] = map_manager - - return map_manager - ``` - -=== "Route Planning" - - ```python - def get_route_plan(self, agent_id: str) -> Optional[Tuple[carla.Transform, carla.Transform]]: - """ - Get route plan (spawn-destination pair) for specific agent. - - Parameters - ---------- - agent_id : str - Agent identifier - - Returns - ------- - route : tuple or None - (spawn_transform, destination_transform) or None if not found - """ - if not self.marl_map_manager: - return None - - # Get spawn point info by ID - spawn_points = self.marl_map_manager.get_spawn_points(detail=True) - - for spawn_info in spawn_points: - if spawn_info['id'] == agent_id: - return (spawn_info['transform'], spawn_info['dest']) - - return None - ``` - -## Usage Examples - -=== "Basic MARL Setup" - - ```python - from opencda_marl.core.adapters.map_adapter import MARLMapAdapter - from omegaconf import OmegaConf - import carla - - # Load MARL configuration - config = OmegaConf.load('configs/marl/intersection.yaml') - - # Connect to CARLA - client = carla.Client("localhost", 2000) - client.set_timeout(10.0) - - # Create map adapter - map_adapter = MARLMapAdapter(config, client) - - # Get coordinated spawn points for 4 agents - spawn_points = map_adapter.get_marl_spawn_points( - num_agents=4, - strategy='balanced' - ) - - print(f"Generated {len(spawn_points)} coordinated spawn points:") - for i, spawn_info in enumerate(spawn_points): - print(f" Agent {i}: {spawn_info['id']}") - print(f" Spawn: {spawn_info['transform'].location}") - print(f" Destination: {spawn_info['dest'].location}") - ``` - -=== "OpenCDA Compatibility Mode" - - ```python - # Use adapter in OpenCDA-compatible mode - map_adapter = MARLMapAdapter(config, client) - - # Get standard OpenCDA spawn points - opencda_spawns = map_adapter.get_opencda_spawn_points(num_agents=4) - - # Spawn vehicles using OpenCDA approach - blueprint_library = world.get_blueprint_library() - vehicle_bp = blueprint_library.filter('vehicle.tesla.model3')[0] - - vehicles = [] - for i, spawn_point in enumerate(opencda_spawns): - vehicle = world.try_spawn_actor(vehicle_bp, spawn_point) - if vehicle: - vehicles.append(vehicle) - - # Create OpenCDA MapManager for this vehicle - map_manager = map_adapter.create_opencda_map_manager( - vehicle=vehicle, - config=config.map_manager - ) - print(f"Created OpenCDA MapManager for vehicle {vehicle.id}") - ``` - -=== "Hybrid Approach" - - ```python - # Use both MARL coordination and OpenCDA per-vehicle processing - map_adapter = MARLMapAdapter(config, client) - - # 1. Get coordinated MARL spawn points - marl_spawns = map_adapter.get_marl_spawn_points( - num_agents=4, - strategy='balanced' - ) - - # 2. Spawn vehicles at coordinated positions - vehicles = [] - for spawn_info in marl_spawns: - vehicle = world.try_spawn_actor(vehicle_bp, spawn_info['transform']) - if vehicle: - vehicles.append(vehicle) - - # 3. Create OpenCDA MapManagers for individual vehicle processing - map_managers = {} - for vehicle in vehicles: - map_manager = map_adapter.create_opencda_map_manager( - vehicle=vehicle, - config=config.map_manager - ) - map_managers[vehicle.id] = map_manager - - # Each vehicle now has both: - # - Coordinated spawn/destination from MARL - # - Individual OpenCDA MapManager for perception/planning - ``` - -=== "Route Planning Integration" - - ```python - # Get route plans for coordinated agents - spawn_points = map_adapter.get_marl_spawn_points(num_agents=4, strategy='balanced') - - for spawn_info in spawn_points: - agent_id = spawn_info['id'] - - # Get detailed route plan - route = map_adapter.get_route_plan(agent_id) - if route: - spawn_transform, dest_transform = route - - print(f"Agent {agent_id} route plan:") - print(f" Start: ({spawn_transform.location.x:.1f}, {spawn_transform.location.y:.1f})") - print(f" End: ({dest_transform.location.x:.1f}, {dest_transform.location.y:.1f})") - - # Calculate route distance - distance = spawn_transform.location.distance(dest_transform.location) - print(f" Distance: {distance:.1f} meters") - ``` - -## Spawn Strategies - -The adapter supports multiple spawn strategies for different research needs: - -=== "Balanced Strategy" - - ```python - # Distribute agents evenly across intersection arms - spawn_points = map_adapter.get_marl_spawn_points( - num_agents=4, - strategy='balanced' - ) - # Result: One agent per intersection arm for balanced coordination - ``` - -=== "Conflict Strategy" - - ```python - # Create challenging coordination scenarios - spawn_points = map_adapter.get_marl_spawn_points( - num_agents=4, - strategy='conflict' - ) - # Result: Spawn points selected to maximize potential conflicts/interactions - ``` - -=== "Random Strategy" - - ```python - # Random spawn selection for variability - spawn_points = map_adapter.get_marl_spawn_points( - num_agents=4, - strategy='random' - ) - # Result: Randomly selected spawn points with destinations - ``` - -## Visualization and Debugging - -=== "Spawn Point Visualization" - - ```python - # Visualize both MARL and OpenCDA spawn points - map_adapter.visualize_spawn_points( - mode='both', # 'marl', 'opencda', or 'both' - life_time=30.0 - ) - - # MARL spawn points: Magenta (spawn) and Black (destination) - # OpenCDA spawn points: Yellow markers - # Junction centers: Red with green bounding boxes - ``` - -=== "Detailed Junction Visualization" - - ```python - # Access underlying MARL map manager for detailed visualization - marl_manager = map_adapter.marl_map_manager - - # Draw junction analysis - marl_manager.draw_junction_centers(life_time=60.0) - - # Draw spawn-destination connections - marl_manager.draw_spawn_points(life_time=60.0) - - # Print junction information - for junction in marl_manager.get_info()['junctions']: - print(f"Junction {junction['id']}:") - print(f" Center: {junction['center']}") - print(f" Connections: {len(junction['connections'])}") - ``` - -## Configuration Integration - -=== "Map Configuration" - - ```yaml - # configs/marl/intersection.yaml - map: - name: intersection # Map to load - safe_distance: 5.0 # Base spacing for spawn points - spawn_offset: 2 # Spawn distance from junction (multiples of safe_distance) - dest_offset: 2 # Destination distance from junction - spawn_z_lift: 0.3 # Z-axis lift to avoid ground collision - wp_step: 1.0 # Waypoint stepping granularity - - # OpenCDA MapManager configuration (per vehicle) - map_manager: - activate: true # Enable map manager - visualize: false # Disable per-vehicle visualization - pixels_per_meter: 5 # Rasterization resolution - raster_size: [224, 224] # Raster image size - lane_sample_resolution: 2.0 # Lane sampling resolution - ``` - -=== "Spawn Strategy Configuration" - - ```yaml - # MARL-specific spawn configuration - marl: - spawn_strategy: balanced # 'balanced', 'conflict', 'random' - agents: - num_agents: 4 # Number of agents to spawn - ``` - -## Compatibility Matrix - -| Feature | OpenCDA Mode | MARL Mode | Hybrid Mode | -|---------|--------------|-----------|-------------| -| **Map Loading** | ✅ Standard CARLA | ✅ Via MARLMapManager | ✅ MARLMapManager | -| **Spawn Points** | ✅ CARLA built-in | ✅ Coordinated pairs | ✅ Both available | -| **Route Planning** | ❌ Not available | ✅ Spawn-destination pairs | ✅ MARL coordination | -| **Per-Vehicle Maps** | ✅ Standard MapManager | ❌ Not needed | ✅ OpenCDA MapManagers | -| **Visualization** | ✅ Per-vehicle BEV | ✅ Junction/spawn debug | ✅ Both systems | - - ---- - -**Location**: `opencda_marl/core/adapters/map_adapter.py` \ No newline at end of file diff --git a/docs/api/opencda-marl/adapters/vehicle_adapter.md b/docs/api/opencda-marl/adapters/vehicle_adapter.md index eb3950d..ba5de52 100644 --- a/docs/api/opencda-marl/adapters/vehicle_adapter.md +++ b/docs/api/opencda-marl/adapters/vehicle_adapter.md @@ -1,6 +1,6 @@ # Vehicle Adapter API -!!! info "Implementation Status" +!!! success "Implementation Status" The Vehicle Adapter is **fully implemented** and provides the core bridge between OpenCDA's VehicleManager and MARL multi-agent control systems. The MARLVehicleAdapter is the central component that enables individual vehicles to be controlled by different agent types while preserving OpenCDA's proven autonomous driving capabilities. It supports multiple controller types and provides seamless integration for reinforcement learning agents. @@ -39,33 +39,30 @@ MARLVehicleAdapter ```python def __init__( self, - blueprint: carla.ActorBlueprint, - transform: carla.Transform, - world: carla.World, - config: Dict, - cav_world: CavWorld, - destination: carla.Transform = None, - marl_id: str = None + config: Dict[str, Any], + vehicle: carla.Actor, + carla_map, + cav_world, + dump_data: bool = False, + agent_type: str = "behavior" ): """ Initialize MARL vehicle adapter. - + Parameters ---------- - blueprint : carla.ActorBlueprint - Vehicle blueprint for CARLA spawning - transform : carla.Transform - Initial spawn transform - world : carla.World - CARLA world instance config : dict - Vehicle configuration + Vehicle and agent configuration + vehicle : carla.Actor + Spawned CARLA vehicle actor + carla_map : carla.Map + CARLA map instance cav_world : CavWorld CAV world for coordination - destination : carla.Transform, optional - Vehicle destination (for route planning) - marl_id : str, optional - Unique MARL agent identifier + dump_data : bool + Whether to dump data for debugging + agent_type : str + Agent type: "behavior", "vanilla", "rule_based", "marl" """ ``` @@ -80,7 +77,7 @@ The adapter supports multiple controller types for different use cases: 'behavior': "OpenCDA BehaviorAgent (standard autonomous driving)", 'vanilla': "VanillaAgent (enhanced collision avoidance)", 'rule_based': "RuleBasedAgent (3-stage intersection rules)", - 'marl': "MARL-controlled agent (PPO/SAC/TD3 - Planned)" + 'marl': "MARL-controlled agent (TD3/DQN/Q-Learning/MAPPO/SAC)" } ``` @@ -149,7 +146,7 @@ The adapter supports multiple controller types for different use cases: - rotation: Vehicle orientation (yaw, pitch, roll) - speed: Speed magnitude in m/s - acceleration: Current acceleration - - nearby_vehicles: Surrounding vehicle information (planned) + - nearby_vehicles: Surrounding vehicle information Example ------- @@ -204,7 +201,7 @@ The adapter supports multiple controller types for different use cases: === "Basic Adapter Setup" ```python - from opencda_marl.core.adapters.vehicle_adapter import MARLVehicleAdapter + from opencda_marl.core.adapter.vehicle_adapter import MARLVehicleAdapter from opencda.core.common.cav_world import CavWorld import carla @@ -464,4 +461,4 @@ The adapter supports multiple controller types for different use cases: --- -**Location**: `opencda_marl/core/adapters/vehicle_adapter.py` \ No newline at end of file +**Location**: `opencda_marl/core/adapter/vehicle_adapter.py` \ No newline at end of file diff --git a/docs/api/opencda-marl/agent_manager.md b/docs/api/opencda-marl/agent_manager.md index 0240ad7..48ab6c8 100644 --- a/docs/api/opencda-marl/agent_manager.md +++ b/docs/api/opencda-marl/agent_manager.md @@ -2,7 +2,7 @@ The Agent Manager system is the core component responsible for multi-agent coordination in OpenCDA-MARL. It manages the lifecycle of all vehicles and their associated agents, bridging traffic generation with intelligent vehicle control. -!!! info "Implementation Status" +!!! success "Implementation Status" The Agent Manager system is **fully implemented** and provides comprehensive multi-agent management capabilities including spawning, coordination, and lifecycle management. ```text @@ -78,17 +78,20 @@ Agent Management System - Integrates with collision detection system """ - def __init__(self, traffic_manager, config: Dict, cav_world=None): + def __init__(self, config: Dict[str, Any], state: Dict[str, Any], + world: carla.World, cav_world): """ Initialize Agent Manager. - + Parameters ---------- - traffic_manager : MARLTrafficManager - Traffic manager for spawn event generation config : dict Agent configuration including agent_type and behavior settings - cav_world : CavWorld, optional + state : dict + Shared simulation state (traffic events, map data) + world : carla.World + CARLA world instance + cav_world : CavWorld CAV world instance for vehicle management """ ``` @@ -213,7 +216,7 @@ Agent Management System - behavior: Standard OpenCDA BehaviorAgent (fallback) - vanilla: Enhanced collision avoidance agent - rule_based: 3-stage rule-based intersection agent - - marl: MARL agent with algorithm selection (PPO/SAC/TD3) + - marl: MARL agent with RL speed control """ @staticmethod @@ -369,36 +372,25 @@ Agent Management System === "MARLAgent (Reinforcement Learning)" - MARL agent supporting multiple algorithms (PPO, SAC, TD3) for research purposes. + MARL agent with RL-controlled speed. The local planner handles steering and waypoint following; the RL algorithm controls speed only. ```yaml - # MARLAgent configuration (planned) + # MARLAgent configuration agents: - agent_type: "marl" - - agent_kwargs: - algorithm: "PPO" # Algorithm selection: PPO, SAC, TD3 - policy_fn: null # Policy function (loaded model) - blend: 0.7 # Action blending with baseline - clip_kmh: [0.0, 70.0] # Speed clipping range - - # Algorithm-specific parameters - ppo: - learning_rate: 3e-4 - clip_ratio: 0.2 - entropy_coef: 0.01 - - sac: - learning_rate: 3e-4 - tau: 0.005 - alpha: 0.2 + agent_type: "marl" + + MARL: + algorithm: "td3" # td3, dqn, q_learning, mappo, sac + state_dim: 8 # Observation vector dimension + action_dim: 1 # Speed control only + training: true ``` **Features:** - - Multiple RL algorithm support - - Policy function integration - - Action blending with baseline agents - - Configurable observation/action spaces + + - Five RL algorithms: TD3, DQN, Q-Learning, MAPPO, SAC + - Speed-only control (local planner handles steering) + - Configurable observation features via ObservationExtractor - Training and evaluation modes ## Multi-Agent Coordination Examples @@ -562,12 +554,12 @@ Agent Management System # Cruising max_speed: 35 - # MARL agent configuration (planned) - agent_kwargs: - algorithm: "PPO" - policy_fn: null - blend: 0.7 - clip_kmh: [0.0, 70.0] + # MARL algorithm configuration + MARL: + algorithm: "td3" + state_dim: 8 + action_dim: 1 + training: true ``` ## Integration Points diff --git a/docs/api/opencda-marl/benchmark.md b/docs/api/opencda-marl/benchmark.md index 9de07ef..e7f1d1c 100644 --- a/docs/api/opencda-marl/benchmark.md +++ b/docs/api/opencda-marl/benchmark.md @@ -1,6 +1,6 @@ # Benchmark Comparison API -!!! info "Implementation Status" +!!! success "Implementation Status" The Benchmark Comparison System is **fully implemented** and provides comprehensive automated testing for comparing agent performance across different traffic scenarios. The BenchmarkComparator provides a unified system for benchmarking different agent types (behavior, vanilla, rule_based) across standardized traffic scenarios (safe, balanced, aggressive) with comprehensive performance metrics. diff --git a/docs/api/opencda-marl/coordinator.md b/docs/api/opencda-marl/coordinator.md index 67036b0..dab4cca 100644 --- a/docs/api/opencda-marl/coordinator.md +++ b/docs/api/opencda-marl/coordinator.md @@ -2,15 +2,15 @@ The MARL Coordinator is the central orchestrator for multi-agent reinforcement learning scenarios in OpenCDA-MARL. It provides unified control over simulation execution, managing the interaction between scenario management, environment interfaces, and agent execution. -!!! info "Implementation Status" - The MARL Coordinator is **fully implemented** and provides the primary interface for MARL scenario execution with multiple modes: GUI debugging, training, evaluation, and interactive CLI. +!!! success "Implementation Status" + The MARL Coordinator is **fully implemented** and provides the primary interface for MARL scenario execution. It supports CLI training mode and PySide6 GUI mode with real-time visualization. ```text MARLCoordinator -├── Scenario Management # ScenarioBuilder → ScenarioManager -├── Environment Interface # Gym wrapper for standard RL API -├── Agent Management # RL algorithms and policies -├── Execution Modes # GUI, Training, Evaluation, CLI +├── Scenario Management # MARLScenarioManager setup and control +├── Environment Interface # MARLEnv for RL training loop +├── Agent Management # MARLManager for RL algorithms +├── Execution Modes # Training, Evaluation, GUI └── Callback System # Pre/post step and episode hooks ``` @@ -18,16 +18,18 @@ The coordinator follows a clear architectural flow: ```mermaid graph TD - A[MARLCoordinator] --> B[ScenarioBuilder] - B --> C[ScenarioManager] - C --> D[GymEnvironment] - D --> E[AgentManager] - - A --> F[ExecutionMode] - F --> G[GUI Debug] - F --> H[Training] - F --> I[Evaluation] - F --> J[Interactive CLI] + A[MARLCoordinator] --> B[MARLScenarioManager] + B --> C[MARLAgentManager] + B --> D[MARLTrafficManager] + + A --> E[MARLEnv] + E --> F[MARLManager] + F --> G[RL Algorithms] + + A --> H[Execution Mode] + H --> I[Training] + H --> J[Evaluation] + H --> K[GUI Debug] ``` ## Core Classes @@ -40,11 +42,11 @@ graph TD class MARLCoordinator: """ High-level coordinator for MARL experiments. - + Orchestrates interaction between: - Scenario management (CARLA simulation) - - Agent management (RL policies) - - Environment interface (Gym API) + - MARLEnv (RL training environment) + - MARLManager (RL algorithms and policies) - User interfaces (GUI/CLI) """ ``` @@ -52,39 +54,17 @@ graph TD === "Constructor" ```python - def __init__( - self, - config: Dict, - mode: ExecutionMode = ExecutionMode.DEMO, - enable_gui: bool = False - ): + def __init__(self, config: Dict): """ Initialize MARL Coordinator. - + Parameters ---------- config : dict - Combined OpenCDA and MARL configuration - mode : ExecutionMode - Execution mode (TRAINING, GUI_DEBUG, EVALUATION, DEMO) - enable_gui : bool - Whether to enable GUI interface + Combined OpenCDA and MARL configuration (OmegaConf) """ ``` -=== "ExecutionMode" - - Enumeration defining different execution modes for the coordinator. - - ```python - class ExecutionMode(Enum): - """Execution modes for MARL coordinator.""" - TRAINING = "training" # Automated RL training - GUI_DEBUG = "gui_debug" # Step-by-step GUI control - EVALUATION = "evaluation" # Policy evaluation - DEMO = "demo" # Demonstration mode - ``` - === "Key Methods" === "Initialization" @@ -92,257 +72,193 @@ graph TD ```python def initialize(self): """ - Initialize all components following the proper architecture: - Coordinator -> ScenarioBuilder -> ScenarioManager -> GymEnv + Initialize all components following the architecture: + Coordinator -> MARLScenarioManager -> MARLEnv -> MARLManager + + Sets up CARLA client, world, scenario manager, + environment, and RL algorithm manager. """ - # 1. Create CAV world - self.cav_world = CavWorld(apply_ml=False) - - # 2. Use ScenarioBuilder to create appropriate scenario manager - self.scenario_manager = self.scenario_builder.build_from_config( - config=self.config, - cav_world=self.cav_world - ) - - # 3. Wrap scenario manager with Gym environment - self.gym_env = MARLGymEnv( - scenario_manager=self.scenario_manager, - config=self.config, - render_mode='human', - max_episode_steps=500 - ) ``` === "Step Execution" ```python - def step(self, external_actions: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: + def step(self) -> Dict[str, Any]: """ - Execute one coordinated step through Gym environment. - - Parameters - ---------- - external_actions : dict, optional - External actions (from GUI or specific algorithms) - + Execute one coordinated step through MARLEnv. + Returns ------- step_info : dict - Complete step information with Gym format + Complete step information including: + - step: Current step number + - episode: Current episode number + - observations: Agent observations + - rewards: Agent rewards + - events: Step events (collisions, completions) + - metrics: Training metrics """ - # Execute pre-step callbacks - for callback in self.pre_step_callbacks: - callback(self) - - # Get actions (from external source or agents) - if external_actions: - actions = external_actions - else: - current_obs = self.gym_env._get_observations() - actions = self.agent_manager.get_actions(current_obs) - - # Execute step through Gym environment - observations, rewards, dones, truncated, info = self.gym_env.step(actions) - - # Combine information and execute post-step callbacks - step_info = { - 'step': self.current_step, - 'episode': self.current_episode, - 'actions': actions, - 'observations': observations, - 'rewards': rewards, - 'dones': dones, - 'truncated': truncated, - 'metrics': info.get('metrics', {}), - 'gym_info': info - } - - for callback in self.post_step_callbacks: - callback(self, step_info) - - return step_info ``` === "Episode Management" ```python def reset_episode(self) -> Dict[str, Any]: - """Reset for new episode through Gym environment.""" - initial_observations, info = self.gym_env.reset() - - self.current_step = 0 - self.current_episode += 1 - - # Execute episode callbacks - for callback in self.episode_callbacks: - callback(self) - - return { - 'episode': self.current_episode, - 'step': self.current_step, - 'observations': initial_observations, - 'agent_ids': info.get('agent_ids', []), - 'gym_info': info - } + """ + Reset for new episode. + + Returns + ------- + episode_info : dict + Initial episode state with observations + """ ``` + === "Run Training" + + ```python + def run(self): + """ + Main execution loop. + + Handles full training/evaluation lifecycle: + - Episode loop with configurable max_episodes + - Step loop with configurable max_steps + - Checkpoint saving and metrics logging + - World reset at configurable intervals + """ + ``` + === "GUI Mode" + + ```python + def run_gui_mode(self): + """ + Launch PySide6 GUI dashboard for interactive control. + + Provides real-time visualization of: + - Agent observations and rewards + - Training metrics and plots + - Step-by-step execution control + """ + ``` ## Usage Examples === "Basic Usage" ```python - from opencda_marl.core.coordinator import MARLCoordinator, ExecutionMode + from opencda_marl.coordinator import MARLCoordinator from omegaconf import OmegaConf - + # Load configuration - config = OmegaConf.load('configs/marl/intersection.yaml') - - # Create coordinator - coordinator = MARLCoordinator( - config=config, - mode=ExecutionMode.DEMO + config = OmegaConf.merge( + OmegaConf.load('configs/marl/default.yaml'), + OmegaConf.load('configs/marl/td3_simple_v4.yaml') ) - - # Initialize all components + + # Create and initialize coordinator + coordinator = MARLCoordinator(config=config) coordinator.initialize() - - # Run a few steps - for _ in range(10): - step_info = coordinator.step() - print(f"Step {step_info['step']}: Rewards = {step_info['rewards']}") + + # Run training + coordinator.run() ``` === "Training Mode" ```python - # Training mode with automated episode management - coordinator = MARLCoordinator( - config=config, - mode=ExecutionMode.TRAINING + # Training with specific algorithm + config = OmegaConf.merge( + OmegaConf.load('configs/marl/default.yaml'), + OmegaConf.load('configs/marl/td3_simple_v4.yaml') ) - + # Ensure training mode + config.MARL.training = True + + coordinator = MARLCoordinator(config=config) coordinator.initialize() - - # Run training for 100 episodes - results = coordinator.run_training(num_episodes=100) - - # Analyze results - avg_reward = sum(r['total_reward'] for r in results) / len(results) - print(f"Average reward over training: {avg_reward:.2f}") + coordinator.run() ``` === "GUI Debug Mode" ```python - # GUI mode with step-by-step control - coordinator = MARLCoordinator( - config=config, - mode=ExecutionMode.GUI_DEBUG, - enable_gui=True - ) - + # GUI mode with PySide6 dashboard + coordinator = MARLCoordinator(config=config) coordinator.initialize() - - # Launch GUI interface (blocks until window closes) coordinator.run_gui_mode() ``` -=== "Interactive CLI" +=== "CLI Execution" - ```python - # Interactive command-line interface - coordinator = MARLCoordinator(config=config) - coordinator.initialize() - - # Start interactive session - coordinator.run_interactive_cli() - - # Available commands: - # - step: Execute single step - # - run [n]: Run n steps (default 10) - # - reset: Reset episode - # - train [n]: Run training for n episodes - # - quit: Exit - ``` + ```bash + # Train with TD3 + python opencda.py -t td3_simple_v4 --marl + # Train with GUI + python opencda.py -t td3_simple_v4 --marl --gui + # Evaluate with vanilla baseline + python opencda.py -t vanilla --marl + ``` ## Integration Points The coordinator serves as the central integration point for all MARL components: -| Component | Integration Method | Purpose | -| ------------------- | -------------------- | ------------------------------------------------ | -| **ScenarioBuilder** | Direct instantiation | Creates scenario managers based on configuration | -| **ScenarioManager** | Via builder | Manages CARLA simulation and vehicle spawning | -| **GymEnvironment** | Wrapper pattern | Provides standard RL API over scenario manager | -| **AgentManager** | Direct integration | Manages RL algorithms and policy execution | -| **GUI Components** | Callback system | Provides visual debugging and control interface | - +| Component | Integration Method | Purpose | +| ---------------------- | -------------------- | ---------------------------------------------- | +| **MARLScenarioManager** | Direct instantiation | Manages CARLA simulation and vehicle spawning | +| **MARLEnv** | Wraps scenario manager | Provides RL training loop (obs, reward, done) | +| **MARLManager** | Via MARLEnv | Manages RL algorithms and policy execution | +| **MARLAgentManager** | Via scenario manager | Multi-agent lifecycle management | +| **GUI Dashboard** | Callback system | PySide6 visual debugging and control | === "Callback System" - The coordinator provides a comprehensive callback system for extending functionality: - - === "Registering Callbacks" - - ```python - def pre_step_callback(coordinator): - """Called before each step execution.""" - print(f"About to execute step {coordinator.current_step + 1}") - - def post_step_callback(coordinator, step_info): - """Called after each step execution.""" - total_reward = sum(step_info['rewards'].values()) - print(f"Step completed. Total reward: {total_reward:.3f}") - - def episode_callback(coordinator): - """Called at start of new episode.""" - print(f"Starting episode {coordinator.current_episode}") - - # Register callbacks - coordinator.register_pre_step_callback(pre_step_callback) - coordinator.register_post_step_callback(post_step_callback) - coordinator.register_episode_callback(episode_callback) - ``` - - === "GUI Integration Example" + The coordinator provides a callback system for extending functionality: - ```python - class CustomGUIController: - def __init__(self, coordinator): - self.coordinator = coordinator - - # Register for step updates - coordinator.register_post_step_callback(self.update_display) - - def update_display(self, coordinator, step_info): - """Update GUI with step information.""" - self.step_label.setText(f"Step: {step_info['step']}") - self.reward_label.setText(f"Reward: {sum(step_info['rewards'].values()):.3f}") - ``` + ```python + def pre_step_callback(coordinator): + """Called before each step execution.""" + print(f"About to execute step {coordinator.current_step + 1}") + + def post_step_callback(coordinator, step_info): + """Called after each step execution.""" + total_reward = sum(step_info['rewards'].values()) + print(f"Step completed. Total reward: {total_reward:.3f}") + + def episode_callback(coordinator): + """Called at start of new episode.""" + print(f"Starting episode {coordinator.current_episode}") + + # Register callbacks + coordinator.pre_step_callbacks.append(pre_step_callback) + coordinator.post_step_callbacks.append(post_step_callback) + coordinator.episode_callbacks.append(episode_callback) + ``` === "Error Handling" - The coordinator implements comprehensive error handling: - ```python try: coordinator.initialize() - step_info = coordinator.step() + coordinator.run() except ValueError as e: print(f"Configuration error: {e}") except RuntimeError as e: print(f"Simulation error: {e}") except KeyboardInterrupt: print("Training interrupted by user") - coordinator.stop() finally: coordinator.close() ``` === "Thread Safety" - The coordinator is **not thread-safe**. If using in multi-threaded environments (e.g., with Ray), create separate coordinator instances for each worker process. \ No newline at end of file + The coordinator is **not thread-safe**. If using in multi-threaded environments, create separate coordinator instances for each worker process. + +--- + +**Location**: `opencda_marl/coordinator.py` diff --git a/docs/api/opencda-marl/environment.md b/docs/api/opencda-marl/environment.md index 0976a90..3eb7d69 100644 --- a/docs/api/opencda-marl/environment.md +++ b/docs/api/opencda-marl/environment.md @@ -1,48 +1,49 @@ # MARL Environment API -!!! info "Implementation Status" - The MARL Environment is **fully implemented** as a custom environment that provides direct integration with CARLA and OpenCDA. +!!! success "Implementation Status" + The MARL Environment is **fully implemented** as a custom environment (non-Gym) that provides direct integration with CARLA and OpenCDA. It manages the full RL loop: observation extraction via ObservationExtractor, action selection via MARLManager, reward calculation, and algorithm updates. -The MARL Environment provides a clean, direct interface for multi-agent reinforcement learning that's perfectly suited for CARLA's dynamic vehicle nature. +The MARL Environment (`MARLEnv`) provides a clean, direct interface for multi-agent reinforcement learning, designed specifically for CARLA's dynamic vehicle nature without Gym dependency. ```text -MARLEnvironment -├── Episode Management # reset_episode(), step execution -├── Observation System # Direct vehicle state access -├── Reward Calculation # Multi-objective reward functions -├── Termination Logic # Collision, completion, timeout -└── MARLTrainer # Training and evaluation workflows +MARLEnv +├── Episode Management # reset_episode(), step execution +├── Observation System # ObservationExtractor for feature vectors +├── Reward Calculation # Multi-objective configurable rewards +├── Event Tracking # StepEvent-based collision/completion tracking +└── Training Integration # MARLManager for algorithm updates ``` ## Core Classes -=== "MARLEnvironment" +=== "MARLEnv" Custom MARL environment with direct CARLA integration. ```python - class MARLEnvironment: + class MARLEnv: """ Custom MARL Environment for OpenCDA. - - Provides clean, direct interface without Gym's complexity. - Perfect fit for CARLA's dynamic vehicle nature. + + Provides direct RL training loop without Gym dependency. + Integrates with MARLScenarioManager for simulation control + and MARLManager for algorithm orchestration. """ ``` === "Constructor" ```python - def __init__(self, scenario_manager, config: Dict[str, Any]): + def __init__(self, scenario_manager: MARLScenarioManager, config: Dict = {}): """ Initialize MARL Environment. - + Parameters ---------- scenario_manager : MARLScenarioManager - The scenario manager to wrap + The scenario manager providing CARLA simulation control config : dict - Environment configuration + MARL configuration (reward params, training settings, etc.) """ ``` @@ -51,238 +52,169 @@ MARLEnvironment === "Episode Management" ```python - def reset_episode(self) -> Dict[str, Any]: + def reset_episode(self): """ Reset for new episode. - - Returns - ------- - episode_info : dict - Initial episode information with observations - - episode: Episode count - - step: Step count (0) - - observations: Initial agent observations - - agent_ids: List of active agent IDs - - active_agents: Number of active agents + + Resets scenario manager, clears event logs, + and prepares for new training episode. """ - - def step(self, actions: Dict[str, Any]) -> Dict[str, Any]: + + def step(self): """ - Execute environment step. - - Parameters - ---------- - actions : dict - Actions for agents (agent_id -> action) - - Returns - ------- - step_info : dict - Complete step information - - step: Current step - - episode: Current episode - - observations: Agent observations - - rewards: Agent rewards - - dones: Agent termination states - - scenario_info: Scenario manager results - - agent_ids: Active agent IDs - - active_agents: Number of active agents - - done: Episode termination - - truncated: Maximum steps reached + Execute one environment step. + + Runs the full RL loop: + 1. Scenario manager step (vehicle control, traffic) + 2. Collect observations via ObservationExtractor + 3. Calculate rewards from step events + 4. MARLManager action selection and algorithm update + + Returns internally managed state; use getter methods + for observations, rewards, and events. """ ``` === "Observation System" ```python - def _get_observations(self) -> Dict[str, np.ndarray]: + def get_observations(self) -> Dict: """ Get observations for all active agents. - + + Returns observations extracted by ObservationExtractor + with configurable feature types. + Returns ------- observations : dict Agent observations (agent_id -> observation vector) - - Observation Vector Format: - [0-2]: Position (x, y, z) - [3-5]: Velocity (x, y, z) - [6-8]: Rotation (pitch, yaw, roll) - [9]: Speed magnitude + """ + + def get_training_info(self) -> Dict[str, Any]: + """ + Get current training information from MARLManager. + + Returns algorithm-specific training stats + (losses, exploration noise, Q-values, etc.) """ ``` -=== "Reward Calculation" +=== "Reward System" ```python - def _calculate_rewards(self, scenario_result: Dict) -> Dict[str, float]: + def get_current_step_rewards(self) -> Dict[str, float]: """ - Calculate rewards for all agents. - - Multi-objective reward function: - 1. Forward progress reward: min(speed / 10.0, 1.0) - 2. Safety reward: +1.0 if distance > 5.0m, -5.0 if < 2.0m - 3. Efficiency reward: +2.0 for ideal speed range (5-15 m/s) - + Get rewards from the current step. + Returns ------- rewards : dict Agent rewards (agent_id -> reward value) """ - ``` -=== "Termination Logic" - - ```python - def _check_termination(self) -> Dict[str, bool]: + def get_reward_params(self) -> Dict: """ - Check termination conditions for each agent. - - Returns - ------- - dones : dict - Agent termination states (agent_id -> done boolean) - - Termination Conditions: - - Collision (instant destruction enabled) - - Destination reached (planned) - - Simulation error + Get current reward function parameters. + + Returns the configurable reward weights and values. """ ``` -## MARLTrainer Integration - -=== "MARLTrainer Class" + Multi-objective reward components (configurable via YAML): - ```python - class MARLTrainer: - """ - Custom MARL Trainer for multi-agent scenarios. - - Provides training/evaluation loops without Gym dependency. - """ - - def __init__(self, environment: MARLEnvironment): - """Initialize trainer with environment.""" - ``` + | Component | Default Value | Description | + | ------------- | ------------- | ------------------------------------- | + | Collision | -500 | Terminal penalty on collision | + | Success | +400 | Terminal reward on reaching destination | + | Step penalty | -0.5 | Per-step cost for efficiency | + | Speed bonus | +1.0 | Reward for maintaining target speed | + | Progress | scaled | Based on distance to destination | + | Stop penalty | -3.0 | Penalty for stopped vehicles | + | Yielding | +1.0 | Reward for yielding to obstacles | -=== "Training Methods" +=== "Event Tracking" ```python - def train_episode(self, agents: Dict[str, Any] = None) -> Dict[str, Any]: + def get_current_events(self) -> List: """ - Train single episode. - - Parameters - ---------- - agents : dict, optional - Agent instances for training (agent_id -> agent) - + Get step events from current simulation step. + Returns ------- - episode_stats : dict - Episode training statistics - - episode: Episode number - - steps: Number of steps - - rewards: Per-agent rewards - - total_reward: Sum of all rewards - - avg_reward: Average reward per agent - - active_agents: Number of agents + events : list[StepEvent] + Events including collisions, completions, timeouts """ - - def train_multiple_episodes(self, num_episodes: int, agents: Dict[str, Any] = None) -> List[Dict]: - """Train multiple episodes and return statistics.""" - - def evaluate(self, num_episodes: int = 10, agents: Dict[str, Any] = None) -> Dict[str, Any]: + + def get_episode_metrics(self) -> Dict: """ - Evaluate agents over multiple episodes. - - Returns - ------- - evaluation_summary : dict - - episodes: Number of evaluation episodes - - avg_reward: Average reward across episodes - - avg_steps: Average steps per episode - - success_rate: Rate of positive reward episodes - - detailed_results: Full episode statistics + Get aggregated episode metrics. + + Returns metrics like total reward, collision count, + success count, average speed, etc. """ ``` +## Observation Features + +The `ObservationExtractor` supports 9 configurable feature types: + +| Feature | Description | +| -------------------------- | ------------------------------------ | +| `rel_x`, `rel_y` | Relative position to ego | +| `heading` | Vehicle orientation (radians) | +| `speed` | Current velocity | +| `distance_to_intersection` | Remaining distance to junction | +| `distance_to_front` | Distance to nearest vehicle ahead | +| `lane_position` | Lateral position in lane | +| `waypoint_buffer` | Next waypoint distance | +| `min_ttc` | Minimum time-to-collision | +| `distance_to_destination` | Remaining route distance | + ## Usage Examples -=== "Basic Training Setup" +=== "Basic Setup" ```python - from opencda_marl.core.marl_environment import MARLEnvironment, MARLTrainer - - # Create environment with scenario manager - environment = MARLEnvironment(scenario_manager, config) - trainer = MARLTrainer(environment) - - # Single episode training - episode_stats = trainer.train_episode() - print(f"Episode reward: {episode_stats['total_reward']:.3f}") - - # Multi-episode training - results = trainer.train_multiple_episodes(num_episodes=100) - avg_reward = sum(r['avg_reward'] for r in results) / len(results) - print(f"Training average reward: {avg_reward:.3f}") - ``` + from opencda_marl.envs.marl_env import MARLEnv -=== "Agent Integration" + # Created automatically by coordinator, but can be used directly + env = MARLEnv(scenario_manager, config=marl_config) - ```python - # Training with RL agents (planned) - agents = { - 'agent_1': PPOAgent(observation_space, action_space), - 'agent_2': PPOAgent(observation_space, action_space) - } - - # Training loop with agents - for episode in range(1000): - episode_stats = trainer.train_episode(agents) - - # Update agent policies based on episode results - for agent_id, agent in agents.items(): - agent.update(episode_stats['rewards'][agent_id]) + # Training loop + env.reset_episode() + for step in range(max_steps): + env.step() + + observations = env.get_observations() + rewards = env.get_current_step_rewards() + events = env.get_current_events() ``` -=== "Evaluation Workflow" +=== "With Coordinator (Recommended)" ```python - # Evaluate trained agents - evaluation_results = trainer.evaluate( - num_episodes=50, - agents=trained_agents - ) - - print(f"Evaluation Results:") - print(f" Average Reward: {evaluation_results['avg_reward']:.3f}") - print(f" Success Rate: {evaluation_results['success_rate']:.3f}") - print(f" Average Steps: {evaluation_results['avg_steps']:.1f}") + from opencda_marl.coordinator import MARLCoordinator + + # Coordinator creates and manages MARLEnv internally + coordinator = MARLCoordinator(config=config) + coordinator.initialize() + + # MARLEnv is accessible via coordinator + coordinator.run() # Handles full training loop ``` -=== "Custom Reward Function" +=== "Custom Reward Configuration" - ```python - class CustomMARLEnvironment(MARLEnvironment): - """Custom environment with specialized reward function.""" - - def _calculate_rewards(self, scenario_result: Dict) -> Dict[str, float]: - """Custom reward calculation.""" - rewards = {} - - for agent_id, adapter in self.scenario_manager.agents.items(): - reward = 0.0 - - # Custom reward components - reward += self._cooperation_reward(agent_id, adapter) - reward += self._efficiency_reward(agent_id, adapter) - reward += self._safety_reward(agent_id, adapter) - - rewards[agent_id] = reward - - return rewards + ```yaml + # Configure rewards in YAML + rewards: + collision_penalty: -500 + success_reward: 400 + step_penalty: -0.5 + speed_bonus: 1.0 + stop_penalty: -3.0 + yielding_bonus: 1.0 ``` ## Configuration Integration @@ -290,16 +222,18 @@ MARLEnvironment === "Environment Configuration" ```yaml - # MARL environment settings - environment: - max_episode_steps: 1000 # Maximum steps per episode - reward_function: "multi_objective" # Reward function type - observation_type: "vector" # Observation format - - # Reward function parameters - rewards: - progress_weight: 1.0 # Forward progress reward weight - safety_weight: 5.0 # Safety reward weight - efficiency_weight: 2.0 # Efficiency reward weight - cooperation_weight: 1.0 # Cooperation reward weight (planned) - ``` \ No newline at end of file + # MARL environment settings in config + scenario: + max_steps: 2400 # Maximum steps per episode + max_episodes: 500 # Maximum training episodes + + MARL: + algorithm: "td3" # RL algorithm selection + state_dim: 8 # Observation vector dimension + action_dim: 1 # Action dimension (speed control) + training: true # Training vs evaluation mode + ``` + +--- + +**Location**: `opencda_marl/envs/marl_env.py` diff --git a/docs/api/opencda-marl/overview.md b/docs/api/opencda-marl/overview.md index dc9d189..ff7200a 100644 --- a/docs/api/opencda-marl/overview.md +++ b/docs/api/opencda-marl/overview.md @@ -3,46 +3,47 @@ OpenCDA-MARL extends the OpenCDA framework with Multi-Agent Reinforcement Learning capabilities for autonomous driving research. This API documentation covers the MARL-specific components and interfaces built on top of the OpenCDA foundation. !!! success "Implementation Status" - **Phase 1 Foundation: 95% Complete** - Core architecture implemented and ready for RL agent integration. + **Core Framework: Complete** - Architecture, RL algorithms, training infrastructure, and evaluation tools are all implemented and ready for research use. ### Architecture & Implementation ```text -OpenCDA-MARL/ +opencda_marl/ +├── coordinator.py # Central MARL orchestrator ├── core/ -│ ├── coordinator.py # Central MARL orchestrator -│ ├── adapters/ -│ │ └── map_adapter.py # OpenCDA-MARL map bridge -│ └── world/ -│ └── map_manager.py # MARL map management -├── scenarios/ -│ ├── scenario_builder.py # Factory for scenario creation -│ ├── scenario_manager.py # Enhanced scenario management -│ └── templates/ # Scenario templates -│ ├── intersection_template.py # ✅ Implemented -│ ├── highway_template.py # 📋 Placeholder -│ └── parking_template.py # 📋 Placeholder +│ ├── agent_manager.py # Vehicle spawning & adapter management +│ ├── adapter/ +│ │ └── vehicle_adapter.py # Vehicle-agent bridge +│ ├── agents/ # Agent implementations (factory pattern) +│ ├── marl/ # RL algorithms & training infrastructure +│ │ ├── marl_manager.py # Algorithm orchestrator +│ │ ├── extractor.py # Observation feature extraction +│ │ ├── metrics.py # Training metrics & CSV export +│ │ ├── checkpoint.py # Model checkpoint management +│ │ └── algorithms/ # TD3, DQN, Q-Learning, MAPPO, SAC +│ ├── safety/ # Collision detection & avoidance +│ └── traffic/ # Traffic flow management & replay ├── envs/ -│ ├── base_env.py # Abstract Gym environment -│ ├── multi_agent_env.py # Multi-agent Gym wrapper -│ └── single_agent_env.py # Single-agent placeholder -├── gui/ -│ ├── step_controller.py # Step-by-step debugging -│ ├── observation_viewer.py # Real-time observation display -│ └── widgets/ # Reusable GUI components -└── configs/ # MARL-specific configurations +│ ├── marl_env.py # Custom CARLA RL environment +│ ├── sumo_marl_env.py # SUMO-only environment +│ ├── evaluation.py # Episode evaluation +│ └── cross_agent_evaluator.py +├── gui/ # PySide6 Qt dashboard +├── scenarios/ # Scenario templates & management +└── configs/ # MARL-specific YAML configurations ``` -| Component | Status | Description | -| ------------------------------------------- | --------------- | --------------------------------------- | -| **[Coordinator](coordinator.md)** | ✅ Complete | Central orchestrator for MARL execution | -| **[Scenario System](scenario.md)** | ✅ Complete | Template-based scenario generation | -| **[Map Adapter](adapters/map_adapter.md)** | ✅ Complete | OpenCDA-MARL map integration bridge | -| **[Environment Interface](environment.md)** | ✅ Complete | Gym-compatible multi-agent environment | -| **[GUI Components](#)** | ✅ Complete | Visual debugging and control interface | -| **[Vehicle Adapter](#)** | 🔄 Next Priority | RL-OpenCDA vehicle bridge | -| **[Agent Infrastructure](#)** | 📋 Planned | RL algorithm implementations | -| **[Training Pipeline](#)** | 📋 Planned | Distributed training infrastructure | +| Component | Status | Description | +| ------------------------------------------------------ | ---------- | ---------------------------------------- | +| **[Coordinator](coordinator.md)** | ✅ Complete | Central orchestrator for MARL execution | +| **[Scenario System](scenario.md)** | ✅ Complete | Template-based scenario generation | +| **[Agent Manager](agent_manager.md)** | ✅ Complete | Multi-agent lifecycle management | +| **[Map Manager](map_manager.md)** | ✅ Complete | Junction-based spawn point generation | +| **[Vehicle Adapter](adapters/vehicle_adapter.md)** | ✅ Complete | RL-OpenCDA vehicle bridge | +| **[Environment Interface](environment.md)** | ✅ Complete | Custom MARL environment (non-Gym) | +| **[Training Infrastructure](training.md)** | ✅ Complete | Algorithms, checkpoints, metrics | +| **[Traffic Configuration](traffic_config.md)** | ✅ Complete | Flow-based traffic generation | +| **[Benchmark System](benchmark.md)** | ✅ Complete | Cross-agent performance comparison | ## Design Philosophy @@ -57,13 +58,13 @@ OpenCDA-MARL/ - Clear separation between OpenCDA and MARL components - Coordinator-based orchestration - Template-based scenario generation - - Standard Gym environment interface + - Custom environment with direct CARLA integration === "3. Research-Focused" - - Support for multiple MARL algorithms (planned) + - Five RL algorithms (TD3, DQN, Q-Learning, MAPPO, SAC) - Flexible experiment configuration via OmegaConf - Comprehensive callback system for extensibility - - Visual debugging tools for development + - TensorBoard integration and convergence detection ## Core Implementation @@ -72,23 +73,22 @@ OpenCDA-MARL/ The central orchestrator provides unified control over multi-agent scenarios: ```python -from opencda_marl.core.coordinator import MARLCoordinator, ExecutionMode -from omegaconf import OmegaConf +from opencda_marl.coordinator import MARLCoordinator # Load configuration -config = OmegaConf.load('configs/marl/intersection.yaml') - -# Create coordinator with GUI mode -coordinator = MARLCoordinator( - config=config, - mode=ExecutionMode.GUI_DEBUG, - enable_gui=True +config = OmegaConf.merge( + OmegaConf.load("configs/marl/default.yaml"), + OmegaConf.load("configs/marl/td3_simple_v4.yaml") ) -# Initialize all components +# Create and initialize coordinator +coordinator = MARLCoordinator(config=config) coordinator.initialize() -# Run GUI interface for debugging +# Run training +coordinator.run() + +# Or run with GUI coordinator.run_gui_mode() ``` @@ -98,267 +98,93 @@ Template-based scenario generation with factory pattern: ```python from opencda_marl.scenarios.scenario_builder import ScenarioBuilder -from opencda.core.common.cav_world import CavWorld - -# Create scenario builder -builder = ScenarioBuilder() # Build scenario from configuration -cav_world = CavWorld(apply_ml=False) +builder = ScenarioBuilder() scenario_manager = builder.build_from_config(config, cav_world) - -# Or build programmatically -config = builder.build_intersection_scenario( - num_agents=4, - intersection_type='4way', - spawn_strategy='balanced' -) ``` ### 3. Environment Interface -Standard Gym-compatible multi-agent environment: +Custom MARL environment with direct CARLA integration: ```python -from opencda_marl.envs.multi_agent_env import MARLGymEnv - -# Create Gym environment -env = MARLGymEnv( - scenario_manager=scenario_manager, - config=config, - render_mode='human', - max_episode_steps=500 -) +from opencda_marl.envs.marl_env import MARLEnv -# Standard Gym interface -observations, info = env.reset() -observations, rewards, dones, truncated, info = env.step(actions) +# Created automatically by coordinator +env = MARLEnv(scenario_manager, config=marl_config) + +# Step cycle: observe → act → reward → learn +# Handled internally by coordinator.run() ``` -### 4. Map Integration +### 4. RL Algorithms -Hybrid system combining OpenCDA map loading with MARL coordination: +All algorithms share a common `BaseAlgorithm` interface: ```python -from opencda_marl.core.adapters.map_adapter import MARLMapAdapter -import carla - -# Connect to CARLA -client = carla.Client("localhost", 2000) -client.set_timeout(10.0) - -# Create map adapter -map_adapter = MARLMapAdapter(config, client) - -# Get coordinated spawn points for MARL scenarios -spawn_points = map_adapter.get_marl_spawn_points( - num_agents=4, - strategy='balanced' -) - -# Get OpenCDA-compatible spawn points -opencda_spawns = map_adapter.get_opencda_spawn_points(num_agents=4) +from opencda_marl.core.marl.marl_manager import MARLManager -# Create per-vehicle OpenCDA MapManagers -for vehicle in vehicles: - map_manager = map_adapter.create_opencda_map_manager( - vehicle=vehicle, - config=config.map_manager - ) +# Algorithm selected based on config MARL.algorithm +manager = MARLManager(config) +action = manager.select_action(observations, ego_id, training=True) +manager.store_transition(obs, ego_id, action, reward, next_obs, done) +losses = manager.update() ``` -## Current Capabilities - -=== "Intersection Management" - - Complete multi-agent intersection scenarios ready for RL integration: - - ```python - # Create intersection scenario - coordinator = MARLCoordinator(config=config, mode=ExecutionMode.TRAINING) - coordinator.initialize() - - # Run training episodes (placeholder agents currently) - results = coordinator.run_training(num_episodes=100) - - # Or use GUI for debugging - coordinator = MARLCoordinator(config=config, mode=ExecutionMode.GUI_DEBUG) - coordinator.run_gui_mode() - ``` - -=== "Multiple Execution Modes" - - Four execution modes for different research needs: - - ```python - # 1. GUI debugging mode - coordinator = MARLCoordinator(config, ExecutionMode.GUI_DEBUG, enable_gui=True) - coordinator.run_gui_mode() - - # 2. Training mode - coordinator = MARLCoordinator(config, ExecutionMode.TRAINING) - results = coordinator.run_training(num_episodes=1000) - - # 3. Evaluation mode - coordinator = MARLCoordinator(config, ExecutionMode.EVALUATION) - metrics = coordinator.run_evaluation(num_episodes=100) - - # 4. Interactive CLI mode - coordinator = MARLCoordinator(config, ExecutionMode.DEMO) - coordinator.run_interactive_cli() - ``` - -=== "Template-Based Scenario Generation" - - Extensible scenario system with parameter validation: - - ```python - # Get available scenario types - builder = ScenarioBuilder() - available = builder.get_available_scenarios() # ['intersection', 'highway', 'parking'] - implemented = builder.get_implemented_scenarios() # ['intersection'] - - # Create scenarios programmatically - intersection_config = builder.build_intersection_scenario( - num_agents=6, - intersection_type='4way', - traffic_density='high', - weather_conditions='rainy', - spawn_strategy='conflict' # Create challenging scenarios - ) - ``` - -## Planned Components - -=== "🔄 Vehicle Adapter (Next Priority)" - - Bridge between OpenCDA VehicleManager and RL agents: - - ```python - # Planned vehicle adapter interface - from opencda_marl.core.adapters.vehicle_adapter import VehicleAdapter - - # Bridge OpenCDA VehicleManager with RL agents - adapter = VehicleAdapter( - vehicle_manager=opencda_vehicle, - config=agent_config - ) - - # Convert RL actions to vehicle commands - vehicle_action = adapter.action_to_vehicle_command(rl_action) - observation = adapter.get_observation_from_vehicle() - ``` - -=== "📋 RL Algorithm Integration (Planned)" - - Integration with standard RL libraries: - - ```python - # Planned RL integration - from opencda_marl.agents import PPOAgent - from stable_baselines3 import PPO - - # Create RL agents - agent = PPOAgent( - env=env, - policy='MlpPolicy', - learning_rate=3e-4 - ) - - # Training with coordinator - coordinator.set_agent_manager(agent) - results = coordinator.run_training(total_timesteps=1000000) - ``` - - === "📋 Advanced MARL Features (Future)" - - Communication and coordination capabilities: - - ```python - # Planned communication interface - from opencda_marl.communication import V2XCommunication - - # Enable agent communication - comm_system = V2XCommunication( - range=50.0, - message_size=64, - protocol='broadcast' - ) - - coordinator.enable_communication(comm_system) - ``` - ## Configuration System -=== "Current Configuration Support" +=== "Configuration Loading" MARL uses OmegaConf for flexible configuration management: ```yaml - # configs/marl/intersection.yaml - scenario_type: intersection - - map: - name: intersection - safe_distance: 5.0 - spawn_offset: 2 - dest_offset: 2 + # configs/marl/default.yaml - Base configuration + meta: + simulator: "carla" world: - sync_mode: true - client_port: 2000 - fixed_delta_seconds: 0.05 - weather: - sun_altitude_angle: 15 - cloudiness: 0 - - marl: - agents: - num_agents: 4 - agent_type: random - environment: - observation_type: vector - action_type: discrete - reward_function: safety_efficiency - coordination: - enable_communication: true - spawn_strategy: balanced - ``` - -=== "Integration with OpenCDA" + sync_mode: true + client_port: 2000 - Seamless integration with existing OpenCDA configurations: + scenario: + max_steps: 2400 + max_episodes: 500 - ```python - from omegaconf import OmegaConf + MARL: + algorithm: "td3" + state_dim: 8 + action_dim: 1 + training: true - # Load base OpenCDA config - base_config = OmegaConf.load('opencda/scenario_testing/config_yaml/default.yaml') - - # Load MARL extensions - marl_config = OmegaConf.load('configs/marl/intersection.yaml') + agents: + agent_type: "marl" + ``` - # Merge configurations - config = OmegaConf.merge(base_config, marl_config) +=== "Algorithm-Specific Config" - # Use with coordinator - coordinator = MARLCoordinator(config=config) + ```yaml + # configs/marl/td3_simple_v4.yaml - Overrides default.yaml + MARL: + algorithm: "td3" + state_dim: 8 + td3: + learning_rate_actor: 0.0001 + learning_rate_critic: 0.001 + exploration_noise: 0.3 + warmup_steps: 1000 ``` === "Command-Line Interface" - Access MARL functionality through extended `opencda.py`: - ```bash - # Basic MARL scenario execution - python opencda.py -t intersection -v 0.9.15 --marl - - # GUI debugging mode - python opencda.py -t intersection -v 0.9.15 --marl --gui + # MARL training with specific algorithm config + python opencda.py -t td3_simple_v4 --marl - # Training mode (when RL agents are implemented) - python opencda.py -t intersection -v 0.9.15 --marl --train + # With GUI visualization + python opencda.py -t td3_simple_v4 --marl --gui - # Interactive CLI mode - python opencda.py -t intersection -v 0.9.15 --marl --interactive - ``` \ No newline at end of file + # Baseline agent testing + python opencda.py -t vanilla --marl + python opencda.py -t rule_based --marl + ``` diff --git a/docs/api/opencda-marl/scenario.md b/docs/api/opencda-marl/scenario.md index aff3c58..00176bc 100644 --- a/docs/api/opencda-marl/scenario.md +++ b/docs/api/opencda-marl/scenario.md @@ -1,6 +1,6 @@ # Scenario API -!!! info "Implementation Status" +!!! success "Implementation Status" The Scenario system is **fully implemented** with MARLScenarioManager providing enhanced multi-agent capabilities while maintaining full OpenCDA compatibility. The Scenario system provides comprehensive scenario management for multi-agent reinforcement learning, extending OpenCDA's proven scenario management with MARL-specific capabilities like dynamic vehicle spawning, coordinated agent management, and collision handling. @@ -55,17 +55,23 @@ graph TD === "Constructor" ```python - def __init__(self, config: Dict, client: carla.Client, cav_world: CavWorld): + def __init__(self, scenario_params: Dict, apply_ml, + xodr_path: Optional[str] = None, town: Optional[str] = None, + cav_world: Optional[CavWorld] = None): """ Initialize MARL scenario manager. - + Parameters ---------- - config : dict - Scenario configuration - client : carla.Client - CARLA client instance - cav_world : CavWorld + scenario_params : dict + OpenCDA scenario configuration + apply_ml : bool + Whether to apply ML models + xodr_path : str, optional + Path to custom OpenDRIVE map + town : str, optional + CARLA town name + cav_world : CavWorld, optional CAV world for coordination """ ``` @@ -147,18 +153,21 @@ graph TD === "Constructor" ```python - def __init__(self, config: Dict, world: carla.World, map_adapter: MARLMapAdapter): + def __init__(self, config: Dict[str, Any], state: Dict[str, Any], + world: carla.World, cav_world): """ Initialize agent manager. - + Parameters ---------- config : dict Agent configuration + state : dict + Shared simulation state (traffic events, map data) world : carla.World CARLA world instance - map_adapter : MARLMapAdapter - Map adapter for spawn coordination + cav_world : CavWorld + CAV world for coordination """ ``` @@ -226,7 +235,7 @@ graph TD Adapter that bridges OpenCDA VehicleManager with RL agents. Features: - - Multiple controller types (behavior, vanilla, rule_based, rl_agent) + - Multiple controller types (behavior, vanilla, rule_based, marl) - External speed control for RL agents - Collision detection with instant destruction - Observation extraction for RL @@ -238,10 +247,10 @@ graph TD ```python # Available controller types CONTROLLER_TYPES = { - 'placeholder': "OpenCDA BehaviorAgent (development/testing)", - 'rl_agent': "RL-controlled agent (planned)", + 'behavior': "OpenCDA BehaviorAgent (standard autonomous)", + 'vanilla': "VanillaAgent (enhanced collision avoidance)", 'rule_based': "3-stage intersection rules", - 'carla_tm': "CARLA Traffic Manager baseline" + 'marl': "MARL RL-controlled agent (speed control)" } ``` @@ -478,7 +487,7 @@ graph TD ```python # BenchmarkComparator uses MARLScenarioManager through coordinator - from opencda_marl.core.coordinator import MARLCoordinator + from opencda_marl.coordinator import MARLCoordinator def run_agent_test(agent_type, scenario, timeout): """Run benchmark test using coordinator.""" diff --git a/docs/api/opencda-marl/traffic_config.md b/docs/api/opencda-marl/traffic_config.md index ec2e812..68e1863 100644 --- a/docs/api/opencda-marl/traffic_config.md +++ b/docs/api/opencda-marl/traffic_config.md @@ -42,7 +42,7 @@ Traffic Configuration Architecture # Flow-based traffic generation # NOTE: Flow names are just identifiers - actual spawn locations are determined - # by the 'strategy' parameter and MARLMapAdapter.get_marl_spawn_points() + # by the 'strategy' parameter and MARLMapManager spawn point generation flows: - name: "initial_buildup" # Flow identifier (name does NOT control spawn location) rate_vph: 600 # Vehicles per hour for this flow @@ -85,7 +85,7 @@ Traffic Configuration Architecture **Flow names do NOT control where vehicles spawn**. The actual spawn locations are determined by: - **`strategy` parameter**: Controls spawn point selection algorithm - - **MARLMapAdapter**: Provides available spawn points based on map topology + - **MARLMapManager**: Provides available spawn points based on map topology - **Junction approach detection**: Automatically identifies intersection entry points Available strategies: @@ -318,14 +318,14 @@ The density config files contain **only parameters that override** the base conf - Benchmark integration for record/replay """ - def __init__(self, map_adapter, traffic_cfg: Dict, world: carla.World = None): + def __init__(self, world: carla.World, traffic_cfg: Dict, state: Dict): """ Initialize traffic manager. - + Parameters ---------- - map_adapter : MARLMapAdapter - Map adapter for spawn point generation + world : carla.World + CARLA world instance traffic_cfg : dict Traffic configuration with 'flows' array world : carla.World diff --git a/docs/api/opencda-marl/training.md b/docs/api/opencda-marl/training.md index 92ff1a7..1864bb9 100644 --- a/docs/api/opencda-marl/training.md +++ b/docs/api/opencda-marl/training.md @@ -2,70 +2,285 @@ ## Overview -!!! info "Planned Component" - This component is planned for Phase 2-3 development (Q2 2025). Architecture specifications are preliminary. +!!! success "Implementation Status" + The Training Infrastructure is **fully implemented** with five RL algorithms, checkpoint management, metrics tracking, TensorBoard integration, and convergence detection. -The MARL Training Infrastructure provides distributed training capabilities for multi-agent reinforcement learning on OpenCDA scenarios using Ray/RLlib and other popular frameworks. +The MARL Training Infrastructure provides the complete pipeline for training and evaluating reinforcement learning agents in OpenCDA-MARL scenarios. -## Planned Architecture +```text +Training Infrastructure +├── MARLManager # Algorithm orchestrator +├── BaseAlgorithm # Common algorithm interface +├── Algorithms # TD3, DQN, Q-Learning, MAPPO, SAC +├── ObservationExtractor # Feature extraction for RL +├── CheckpointManager # Model saving/loading +├── TrainingMetrics # Episode statistics & CSV export +├── Replay Buffers # Smart, Prioritized, Rollout +└── TensorBoard # Training visualization +``` -### MARLTrainer Class -Unified training interface supporting multiple algorithms: +## Supported Algorithms -```python -# Planned interface -from opencda_marl.training import MARLTrainer +| Algorithm | Type | Status | Description | +|-----------|------|--------|-------------| +| **TD3** | Continuous, Off-policy | ✅ Implemented | Twin Delayed DDPG with LSTM encoder | +| **DQN** | Discrete, Off-policy | ✅ Implemented | Deep Q-Network with target network | +| **Q-Learning** | Discrete, Tabular | ✅ Implemented | Tabular Q-Learning with state bins | +| **MAPPO** | Continuous, On-policy | ✅ Implemented | Multi-Agent PPO with GAE | +| **SAC** | Continuous, Off-policy | ✅ Implemented | Soft Actor-Critic with entropy tuning | -trainer = MARLTrainer( - algorithm="PPO", # PPO, SAC, QMIX, MADDPG - env_config=environment_config, - training_config=training_config, - num_workers=8 -) +## Core Classes -# Distributed training -results = trainer.train( - total_timesteps=1000000, - checkpoint_freq=10000, - eval_freq=50000 -) -``` +=== "MARLManager" -### Supported Algorithms (Planned) + The algorithm orchestrator that selects and manages the active RL algorithm. -| Algorithm | Type | Status | Description | -|-----------|------|--------|-------------| -| PPO | On-policy | 📋 Planned | Proximal Policy Optimization | -| SAC | Off-policy | 📋 Planned | Soft Actor-Critic | -| QMIX | Value-based | 📋 Planned | Q-Mix for cooperation | -| MADDPG | Actor-Critic | 📋 Planned | Multi-Agent DDPG | + ```python + from opencda_marl.core.marl.marl_manager import MARLManager + + manager = MARLManager(config) + + # Select action based on observations + action = manager.select_action( + multi_agent_obs=observations, + ego_agent_id="agent_001", + training=True + ) + + # Store experience + manager.store_transition(obs, ego_id, action, reward, next_obs, done) + + # Update algorithm (returns loss dict) + losses = manager.update() + + # Get training statistics + info = manager.get_training_info() + ``` + +=== "BaseAlgorithm" + + Abstract base class that all algorithms implement: + + ```python + from opencda_marl.core.marl.algorithms.base_algorithm import BaseAlgorithm + + class BaseAlgorithm(ABC): + """Common interface for all RL algorithms.""" + + def select_action(self, state, training=True) -> float: ... + def store_transition(self, state, action, reward, next_state, done): ... + def update(self) -> Dict[str, float]: ... + def reset_episode(self): ... + def get_training_info(self) -> Dict: ... + def save(self, path: str): ... + def load(self, path: str): ... + ``` + + Built-in features: TensorBoard logging, convergence detection, episode metrics. + +=== "ObservationExtractor" + + Converts raw CARLA vehicle data into normalized observation vectors: + + ```python + from opencda_marl.core.marl.extractor import ObservationExtractor + + extractor = ObservationExtractor(config) + obs_vector = extractor.extract(vehicle_data) + ``` + + Supported features: relative position, heading, speed, distance to intersection, + distance to front vehicle, lane position, waypoint buffer, min TTC, distance to destination. + +## Algorithm Details + +=== "TD3" + + The primary algorithm with LSTM multi-agent context encoding. + + ```python + # Architecture: 8D ego state + LSTM-encoded context → speed action [0, 65 km/h] + # Actor: [8+256, 1024, 1024, 512, 256] → LayerNorm → tanh + # Critic: Twin Q-networks for reduced overestimation + ``` + + Key features: + + - LSTM encoder for processing multi-agent observations + - LayerNorm before tanh to prevent gradient vanishing + - Delayed policy updates (every 2 steps) + - Target policy smoothing with noise + - Exploration noise decay (0.3 → 0.05) + - Warmup phase (1000 steps of vanilla agent) + - SmartReplayBuffer or PrioritizedReplayBuffer (configurable) + +=== "DQN" + + Discrete action deep Q-network. + + ```python + # Network: [state_dim, 64, 32] → Q-values for discrete speed actions + # Actions: Predefined speed levels (e.g., [0, 5, 8, 12, 15] m/s) + ``` + + Key features: Epsilon-greedy exploration, target network (updated every 100 steps), gradient clipping. + +=== "MAPPO" + + Multi-Agent Proximal Policy Optimization. + + Key features: Generalized Advantage Estimation (GAE), Gaussian actor for continuous actions, rollout buffer, clipped surrogate objective. + +=== "SAC" + + Soft Actor-Critic with automatic entropy tuning. + + Key features: Entropy-regularized objective, automatic temperature (alpha) tuning, twin Q-networks, reparameterization trick. + +## Checkpoint Management + +=== "CheckpointManager" + + ```python + from opencda_marl.core.marl.checkpoint import CheckpointManager + + checkpoint_mgr = CheckpointManager(config) + + # Save checkpoints (automatic in training) + checkpoint_mgr.save(algorithm, episode=100, reward=350.0) + + # Load checkpoints + checkpoint_mgr.load(algorithm, mode="latest") # or "best" + ``` + + Saves three types: `latest_checkpoint.pth`, `best_checkpoint.pth`, `episode_XXXX.pth` + +=== "Directory Structure" + + ```text + checkpoints/ + └── td3_simple_v4/ + ├── latest_checkpoint.pth + ├── best_checkpoint.pth + ├── episode_0100.pth + └── metadata.json + ``` + +## Metrics & Logging + +=== "TrainingMetrics" + + ```python + from opencda_marl.core.marl.metrics import TrainingMetrics + + metrics = TrainingMetrics(config) + ``` + + Tracks per episode: total reward, success/collision rates, average speed, target speed gap, throughput, near-miss count, TTC violations. + + Exports to CSV in `metrics_history/` directory. + +=== "TensorBoard" + + Metrics logged to TensorBoard: + + - **Loss/critic**, **Loss/actor**: Training losses + - **Q_values/Q1_mean**, **Q_values/Q2_mean**: Value estimates + - **Gradients/critic_pre_clip**, **Gradients/actor_pre_clip**: Gradient norms + - **TD3/exploration_noise**: Current noise level + - **Learning/reward_moving_avg**: Smoothed reward trend + - **Safety/near_miss_count**, **Safety/ttc_violation_rate**: Safety metrics + + ```bash + # View training progress + pixi run tensorboard + # or: tensorboard --logdir=runs + ``` + +=== "Convergence Detection" + + Automatic convergence detection checks: + + - Reward CV < 15% over 10-episode window + - Success rate CV < 20% + - Collision rate improving (second half ≤ first half × 1.1) + - Minimum 20 episodes before checking + +## Replay Buffers + +=== "SmartReplayBuffer" + + Default off-policy buffer with recency bias. + + - Pre-allocated numpy arrays for O(1) random access + - Circular buffer with O(1) push + - Sampling: 50% recent experiences (last 20%) + 50% diverse + - Pre-emptive clearing at 90% capacity + +=== "PrioritizedReplayBuffer" + + Optional TD-error prioritized sampling. + + - Alpha (priority exponent): 0.6 + - Beta annealing: 0.4 → 1.0 for importance sampling + - Enabled via config: `td3.use_per: true` + +=== "RolloutBuffer" + + On-policy buffer for MAPPO. + + - Stores complete episodes for GAE computation + - Cleared after each policy update + +## Usage + +=== "Training" + + ```bash + # Train with TD3 (default) + python opencda.py -t td3_simple_v4 --marl + + # Train with DQN + python opencda.py -t dqn --marl -### Training Features (Planned) + # Train with SAC + python opencda.py -t sac --marl + ``` -- **Distributed Training**: Ray-based parallel training -- **Experiment Management**: MLflow integration for tracking -- **Hyperparameter Tuning**: Ray Tune integration -- **Custom Rewards**: Flexible reward function definitions -- **Scenario Curriculum**: Progressive training difficulty +=== "Evaluation" -## Development Roadmap + ```yaml + # Set training: false in config to run evaluation + MARL: + training: false + ``` -### Phase 2 -- [ ] Basic PPO implementation -- [ ] Single-machine training -- [ ] Experiment logging + The CheckpointManager will load the best model automatically when training is disabled. -### Phase 3 -- [ ] Distributed training with Ray -- [ ] Multiple algorithm support -- [ ] Hyperparameter optimization -- [ ] Performance benchmarking +=== "Configuration Example" -### Phase 4 -- [ ] Advanced algorithms (QMIX, MADDPG) -- [ ] Custom environment curriculum -- [ ] Production deployment tools + ```yaml + # configs/marl/td3_simple_v4.yaml + MARL: + algorithm: "td3" + state_dim: 8 + action_dim: 1 + training: true + td3: + learning_rate_actor: 0.0001 + learning_rate_critic: 0.001 + batch_size: 256 + memory_size: 100000 + gamma: 0.99 + tau: 0.005 + exploration_noise: 0.3 + noise_decay: 0.998 + min_noise: 0.05 + warmup_steps: 1000 + lstm_hidden: 256 + ``` --- -**Status**: 📋 Planned | **Target**: End of August 2025 \ No newline at end of file +- **Location**: `opencda_marl/core/marl/` +- **Algorithms**: `opencda_marl/core/marl/algorithms/` diff --git a/docs/images/OpenCDA_MARL_architecture.png b/docs/images/OpenCDA_MARL_architecture.png index 97794be..d6f5865 100644 Binary files a/docs/images/OpenCDA_MARL_architecture.png and b/docs/images/OpenCDA_MARL_architecture.png differ diff --git a/docs/marl/architecture.md b/docs/marl/architecture.md index 259bc28..80dd2f9 100644 --- a/docs/marl/architecture.md +++ b/docs/marl/architecture.md @@ -2,13 +2,11 @@ OpenCDA-MARL extends the original OpenCDA framework to support Multi-Agent Reinforcement Learning (MARL) for cooperative autonomous driving. This document describes the current architecture implementation. -!!! info "Development Status" - OpenCDA-MARL is in early development (v0.1.0-alpha). The system currently focuses on intersection scenarios with multiple agent types and basic RL algorithms. This is research-oriented code designed for experimentation rather than production use. +!!! success "Release Status" + OpenCDA-MARL v1.0.0 is the first stable release. The system supports intersection scenarios with multiple agent types and five RL algorithms (TD3, DQN, Q-Learning, MAPPO, SAC). Both CARLA and SUMO simulators are supported. ## Directory Structure -This is the directory structure of the OpenCDA-MARL project. -
Directory Structure ```sh @@ -27,47 +25,96 @@ OpenCDA-MARL/ │ ├── customize/ # User customizations │ └── scenario_testing/ # Scenario scripts and configs │ -├── opencda_marl/ # MARL extensions (implemented) +├── opencda_marl/ # MARL extensions │ ├── coordinator.py # Main MARL orchestrator │ ├── core/ # Core MARL components -│ │ ├── adapter/ # OpenCDA integration adapters -│ │ │ └── vehicle_adapter.py # Vehicle-agent bridge +│ │ ├── agent_manager.py # Vehicle spawning & adapter management +│ │ ├── events.py # StepEvent dataclass +│ │ ├── world_reset_manager.py # CARLA memory management +│ │ ├── adapter/ # Vehicle control abstraction +│ │ │ ├── vehicle_adapter.py # MARL vehicle wrapper +│ │ │ ├── vehicle_defaults.py # Default configs +│ │ │ └── exception.py # Custom exceptions │ │ ├── agents/ # Agent implementations -│ │ │ ├── agent_factory.py # Agent factory -│ │ │ ├── basic_agent.py # Base agent -│ │ │ ├── marl_agent.py # MARL agent base +│ │ │ ├── agent_factory.py # Agent factory pattern +│ │ │ ├── basic_agent.py # Base autonomous agent +│ │ │ ├── marl_agent.py # RL-controlled agent │ │ │ ├── marl_behavior_agent.py # Behavior agent -│ │ │ ├── vanilla_agent.py # Safety agent -│ │ │ └── rule_based_agent.py # Rule-based agent -│ │ ├── marl/ # MARL algorithms -│ │ │ └── algorithms/ # RL algorithm implementations -│ │ │ ├── q_learning.py # Q-Learning -│ │ │ ├── dqn.py # Deep Q-Network -│ │ │ └── td3.py # Twin Delayed DDPG +│ │ │ ├── vanilla_agent.py # Safety agent +│ │ │ └── rule_based_agent.py # Rule-based agent +│ │ ├── marl/ # MARL algorithms & infrastructure +│ │ │ ├── marl_manager.py # Algorithm orchestrator +│ │ │ ├── extractor.py # Observation feature extraction +│ │ │ ├── metrics.py # Training metrics tracking +│ │ │ ├── checkpoint.py # Model checkpoint management +│ │ │ └── algorithms/ # RL implementations +│ │ │ ├── base_algorithm.py # Abstract base class +│ │ │ ├── q_learning.py # Q-Learning +│ │ │ ├── dqn.py # Deep Q-Network +│ │ │ ├── td3.py # Twin Delayed DDPG +│ │ │ ├── mappo.py # Multi-Agent PPO +│ │ │ ├── sac.py # Soft Actor-Critic +│ │ │ ├── rollout_buffer.py # MAPPO rollout buffer +│ │ │ └── smart_replay_buffer.py # High-perf replay buffer │ │ ├── plan/ # Planning components -│ │ ├── safety/ # Safety management -│ │ └── traffic/ # Traffic utilities +│ │ ├── safety/ # Collision avoidance +│ │ └── traffic/ # Traffic management system +│ │ ├── traffic_manager.py # Traffic orchestrator +│ │ ├── events.py # SpawnEvent definition +│ │ ├── flows.py # Traffic flow patterns +│ │ ├── planner.py # Route planning +│ │ ├── serializer.py # Event recording/replay (JSON/HDF5) +│ │ ├── sumo_adapter.py # SUMO interface +│ │ └── sumo_spawner.py # SUMO vehicle spawning │ ├── envs/ # MARL environments -│ │ ├── marl_env.py # Main MARL environment -│ │ ├── carla_monitor.py # CARLA monitoring +│ │ ├── marl_env.py # CARLA MARL environment (main RL loop) +│ │ ├── sumo_marl_env.py # SUMO-only environment +│ │ ├── carla_monitor.py # CARLA telemetry │ │ ├── carla_spectator.py # Camera control -│ │ └── evaluation.py # Evaluation system -│ ├── gui/ # GUI system +│ │ ├── evaluation.py # Episode evaluation +│ │ ├── evaluation_plots.py # Visualization plots +│ │ └── cross_agent_evaluator.py # Multi-agent comparison +│ ├── gui/ # PySide6 Qt-based GUI │ │ ├── dashboard.py # Main dashboard │ │ ├── observation_viewer.py # Agent observations -│ │ └── step_controller.py # Simulation control +│ │ ├── step_controller.py # Simulation control +│ │ └── widgets/ # GUI panels +│ │ ├── agent_observation_panel.py +│ │ ├── environment_panel.py +│ │ ├── metrics_display.py +│ │ └── panels/ +│ │ ├── reward_panel.py +│ │ ├── system_panel.py +│ │ ├── traffic_panel.py +│ │ └── weather_panel.py │ ├── scenarios/ # MARL scenario management -│ │ └── scenario_manager.py # Enhanced ScenarioManager -│ │ -│ └── assets/ # MARL-specific assets -│ └── maps/ # Custom intersection maps -│ ├── intersection.xodr # Intersection map file -│ └── intersection.fbx # Intersection 3D model +│ │ ├── scenario_builder.py # Factory for scenarios +│ │ ├── scenario_manager.py # Main scenario orchestrator +│ │ └── templates/ # Scenario templates +│ │ ├── base_template.py +│ │ └── intersection.py +│ ├── assets/ # MARL-specific assets +│ │ ├── maps/ # Custom intersection maps +│ │ │ ├── intersection.xodr +│ │ │ └── intersection.fbx +│ │ └── intersection_sumo/ # SUMO scenario files +│ └── utils/ # Utilities │ -├── configs/ # NEW: Unified configuration +├── configs/ # Unified configuration │ ├── opencda/ # Original OpenCDA configs │ └── marl/ # MARL-specific configs +│ ├── default.yaml # Base configuration +│ ├── td3_simple_v4.yaml # Latest TD3 config +│ ├── dqn.yaml # DQN config +│ ├── mappo.yaml # MAPPO config +│ ├── sac.yaml # SAC config +│ ├── vanilla.yaml # Vanilla baseline +│ ├── behavior.yaml # Behavior baseline +│ ├── rule_based.yaml # Rule-based baseline +│ ├── sumo.yaml # SUMO-only mode +│ └── ... # Additional TD3 variants │ +├── checkpoints/ # Saved model weights └── scripts/ # Installation and setup scripts ```
@@ -78,17 +125,81 @@ OpenCDA-MARL follows a 3-layer architecture that preserves OpenCDA's core functi ![OpenCDA-MARL Architecture](../images/OpenCDA_MARL_architecture.png) +```mermaid +graph TD + subgraph "Layer 3: Algorithms" + TD3[TD3] & DQN[DQN] & QL[Q-Learning] & MAPPO[MAPPO] & SAC[SAC] + RB[Rule-based] & BH[Behavior] & VN[Vanilla] + end + + subgraph "Layer 2: MARL Adapter" + CO[MARLCoordinator] --> ENV[MARLEnv] + CO --> SM[ScenarioManager] + CO --> GUI[Dashboard GUI] + ENV --> MM[MARLManager] + MM --> TD3 & DQN & QL & MAPPO & SAC + SM --> AM[AgentManager] + AM --> VA[VehicleAdapter] + VA --> RB & BH & VN + end + + subgraph "Layer 1: OpenCDA Core" + CARLA[CARLA Simulation] + VM[VehicleManager] + LP[LocalPlanner] + end + + VA --> VM --> LP + SM --> CARLA +``` + ### Layer 1: OpenCDA Core Fully preserved OpenCDA components including CARLA integration, physics simulation, sensor systems (RGB, LiDAR, GPS), vehicle management, V2X communication, and scenario management. This layer remains unchanged from the original OpenCDA framework. ### Layer 2: MARL Adapter Interface -The bridge layer between OpenCDA and MARL algorithms. Key components include the MARLCoordinator (main orchestrator), MARLEnvironment (custom RL environment with CARLA integration), Vehicle Adapters (bridge OpenCDA vehicles with MARL agents), GUI System (dashboard with visualization), and Evaluation System (cross-agent performance comparison). +The bridge layer between OpenCDA and MARL algorithms: + +- **MARLCoordinator**: Main orchestrator — manages simulation lifecycle, episode/step execution, GUI mode, and callback system +- **MARLEnv**: Custom RL environment with direct CARLA integration (observation → action → reward → learn cycle) +- **MARLAgentManager**: Spawns vehicles, manages adapters, handles lifecycle events (success/collision) +- **MARLVehicleAdapter**: Bridges OpenCDA VehicleManager with MARL agent control +- **GUI Dashboard**: PySide6 Qt-based dashboard with real-time visualization and step control +- **Evaluation System**: Episode metrics, cross-agent comparison, evaluation plots ### Layer 3: Algorithm Implementation -Contains both baseline agents and RL algorithms. Baseline agents include Rule-based Agent (3-stage intersection navigation), Behavior Agent (OpenCDA standard driving), and Vanilla Agent (enhanced safety). RL algorithms include Q-Learning (discrete state/action with Q-tables), DQN (Deep Q-Network with neural networks), and TD3 (Twin Delayed DDPG for continuous control). Supports intersection scenarios with custom XODR maps and traffic replay patterns. +=== "RL Algorithms" + + | Algorithm | Type | Description | + |-----------|------|-------------| + | **TD3** | Continuous | Twin Delayed DDPG with LSTM encoder for multi-agent context | + | **DQN** | Discrete | Deep Q-Network with epsilon-greedy exploration | + | **Q-Learning** | Discrete | Tabular Q-Learning with configurable state bins | + | **MAPPO** | On-Policy | Multi-Agent PPO with GAE and rollout buffer | + | **SAC** | Continuous | Soft Actor-Critic with entropy regularization | + +=== "Baseline Agents" + + | Agent | Description | + |-------|-------------| + | **Rule-based** | 3-stage intersection navigation (junction management → car following → cruising) | + | **Behavior** | Simplified OpenCDA behavior cloning with route following | + | **Vanilla** | Enhanced safety with multi-vehicle TTC tracking | + +=== "Training Infrastructure" + + - **MARLManager**: Algorithm orchestrator — selects and manages the active RL algorithm + - **ObservationExtractor**: Converts CARLA vehicle data into normalized RL features + - **CheckpointManager**: Saves latest, best, and episode-specific model weights + - **TrainingMetrics**: Per-episode statistics with CSV export + - **SmartReplayBuffer**: Pre-allocated numpy arrays with recency bias sampling + - **TensorBoard**: Loss, Q-values, gradients, rewards, convergence metrics + - **Convergence Detection**: Coefficient of variation-based (CV < 15% over 10 episodes) + +!!! note "Key Design Decision" + The MARL agent controls **speed only** — the local planner handles steering and waypoint following. This separation simplifies the RL action space while leveraging OpenCDA's proven path planning. ## Configuration System @@ -100,29 +211,73 @@ if opt.marl: default_yaml = "configs/marl/default.yaml" config_yaml = f"configs/marl/{opt.test_scenario}.yaml" -# Direct OmegaConf merge -config = OmegaConf.merge( - OmegaConf.load(default_yaml), - OmegaConf.load(config_yaml) -) + # OmegaConf merge: base defaults + algorithm-specific overrides + config = OmegaConf.merge( + OmegaConf.load(default_yaml), + OmegaConf.load(config_yaml) + ) ``` +Available configuration files cover all algorithms (`td3_simple_v4.yaml`, `dqn.yaml`, `mappo.yaml`, `sac.yaml`), baseline agents (`vanilla.yaml`, `behavior.yaml`, `rule_based.yaml`), and alternative modes (`sumo.yaml`). + ## Execution Flow ```bash -# Basic MARL intersection scenario -python opencda.py -t intersection --marl +# MARL training with TD3 +python opencda.py -t td3_simple_v4 --marl # MARL with GUI visualization -python opencda.py -t intersection --marl --gui +python opencda.py -t td3_simple_v4 --marl --gui # Quick test with pixi pixi run marl-quick-test pixi run marl-quick-test-gui ``` -1. **Configuration Loading**: Load MARL-specific YAML configuration +1. **Configuration Loading**: Merge `default.yaml` + algorithm-specific YAML via OmegaConf 2. **MARLCoordinator**: Create main orchestrator with merged config -3. **Environment Setup**: Initialize CARLA world and MARL environment -4. **Agent Creation**: Setup agents based on configuration -5. **Execution Mode**: Start GUI visualization or automated simulation +3. **Component Initialization**: Create CavWorld, ScenarioManager, MARLEnv +4. **Algorithm Setup**: MARLEnv creates MARLManager, CheckpointManager, TrainingMetrics +5. **Training Loop**: Run episodes × max_steps per episode +6. **Each Step**: Observe → Select Action → Execute → Calculate Reward → Update Algorithm → Log + +```mermaid +sequenceDiagram + participant C as Coordinator + participant E as MARLEnv + participant S as ScenarioManager + participant M as MARLManager + participant A as Algorithm + + C->>E: step() + E->>S: get observations + E->>M: select_action(obs) + M->>A: select_action(obs) + A-->>M: action (target speed) + E->>S: step(target_speeds) + S-->>E: events + new observations + E->>E: calculate_rewards() + E->>A: store_transition() + E->>A: update() + E-->>C: step results +``` + +## Simulator Support + +=== "CARLA Mode (Default)" + + Full autonomous driving simulation with physics, sensors, and 3D visualization. Used for realistic multi-agent training and evaluation. + + ```yaml + meta: + simulator: "carla" # Default + ``` + +=== "SUMO Mode" + + Lightweight traffic-only simulation without CARLA. Useful for large-scale traffic experiments and faster iteration. + + ```yaml + meta: + simulator: "sumo" + ``` diff --git a/docs/marl/changelog/v0.1.0-alpha.md b/docs/marl/changelog/v0.1.0-alpha.md deleted file mode 100644 index 75fbbc3..0000000 --- a/docs/marl/changelog/v0.1.0-alpha.md +++ /dev/null @@ -1,486 +0,0 @@ -# Version 0.1.0-alpha Changelog - -**Release Date**: August 2025 -**Status**: Current Development -**Theme**: Foundation & Framework - -## Major Features - -### MARL Environment System - -=== "Core Environment" - - ```python - # MARLEnv - Custom RL environment with CARLA integration - from opencda_marl.envs.marl_env import MARLEnv - - env = MARLEnv(scenario_manager, config=marl_config) - observations = env.reset() - actions = agent.get_actions(observations) - next_obs, rewards, dones, info = env.step(actions) - ``` - -=== "Features" - - - **Observation System**: Vehicle state extraction for RL agents - - **Reward Calculation**: Multi-objective rewards (progress, safety, efficiency) - - **Termination Logic**: Episode ending based on collision/completion - - **Evaluation Metrics**: Cross-agent performance comparison - - **CARLA Integration**: Direct connection without Gym complexity - -### Multi-Agent Framework - -=== "Agent Types" - - ```python - # Agent Factory - Centralized agent creation - from opencda_marl.core.agents.agent_factory import AgentFactory - - # Four implemented agent types - behavior_agent = AgentFactory.create_agent("behavior", config) - vanilla_agent = AgentFactory.create_agent("vanilla", config) - rule_based_agent = AgentFactory.create_agent("rule_based", config) - marl_agent = AgentFactory.create_agent("marl", config) - ``` - -=== "Agent Capabilities" - - - **Behavior Agent**: OpenCDA standard autonomous driving - - **Vanilla Agent**: Enhanced safety with collision avoidance - - **Rule-based Agent**: 3-stage intersection navigation rules - - **MARL Agent**: RL-based control with algorithm selection - - **Vehicle Adapters**: Bridge OpenCDA vehicles with MARL control - -### RL Algorithm Suite - -=== "Q-Learning" - - ```python - # Q-Learning - Discrete state/action spaces - MARL: - algorithm: "q_learning" - q_learning: - speed_actions: [15, 35, 65] - state_features: - distance_to_intersection: - bins: [0, 5, 15] - epsilon: 0.1 - learning_rate: 0.2 - ``` - -=== "Deep Q-Network (DQN)" - - ```python - # DQN - Neural network Q-value approximation - MARL: - algorithm: "dqn" - state_dim: 7 - dqn: - speed_actions: [30, 45, 60] - learning_rate: 0.001 - memory_size: 50000 - batch_size: 32 - ``` - -=== "TD3 (Twin Delayed DDPG)" - - ```python - # TD3 - Continuous control algorithm - MARL: - algorithm: "td3" - state_dim: 9 - action_dim: 1 - td3: - learning_rate_actor: 0.001 - learning_rate_critic: 0.001 - exploration_noise: 0.5 - ``` - -### GUI Dashboard System - -=== "Main Dashboard" - - ```python - # Qt-based GUI for real-time visualization - from opencda_marl.gui.dashboard import Dashboard - - dashboard = Dashboard(coordinator) - dashboard.show() # Launch interactive GUI - ``` - -=== "GUI Components" - - - **Main Dashboard**: Central control interface with Qt widgets - - **Observation Viewer**: Real-time agent state visualization - - **Step Controller**: Manual simulation stepping and episode management - - **Environment Panel**: CARLA world status and controls - - **Agent Panel**: Individual agent monitoring and control - - **Reward Panel**: Real-time reward tracking and visualization - -### Traffic Management System - -=== "Core Traffic Manager" - - ```python - # MARLTrafficManager - Orchestrates traffic spawn events - from opencda_marl.core.traffic.traffic_manager import MARLTrafficManager - from opencda_marl.core.traffic.flows import TrafficFlow - - traffic_config = { - "mode": "replay", # or "record", "live" - "replay_file": "recordings/lite_2minL.json", - "base_speed": 45.0, - "active_junctions": [4], - "flows": [ - { - "name": "north", - "rate_vph": 200, - "lanes": [0, 1, 2], - "direction": "north", - "start_step": 0, - "end_step": 2400, - "speed_variation": 0.2, - "middle_peak": { - "intensity": 0.4, - "position": 0.5, - "width": 0.3 - } - } - ] - } - - traffic_manager = MARLTrafficManager(world, traffic_config, state) - spawn_events = traffic_manager.update(current_step) - ``` - -=== "Traffic Flow Configuration" - - ```python - # TrafficFlow - Configurable directional traffic patterns - flow = TrafficFlow( - name="east", - lanes=[0, 1, 2], # Lane indices to use - direction="east", # Entry direction - rate_vph=200, # Vehicles per hour per lane - start_step=0, # Start at simulation step 0 - end_step=2400, # End at step 2400 (2 minutes @ 20fps) - speed_variation=0.15, # ±15% speed variation - middle_peak={ # Traffic density peak - "intensity": 0.35, # 35% increase at peak - "position": 0.6, # Peak at 60% of simulation - "width": 0.25 # Peak width (25% of duration) - } - ) - ``` - -=== "Event Recording & Replay" - - ```python - # Event serialization for traffic replay - from opencda_marl.core.traffic.serializer import EventSerializer - - # Record traffic events to JSON format - EventSerializer.save_events_to_json( - events=spawn_events, - filepath="recordings/custom_traffic.json", - config=traffic_config - ) - - # Replay recorded traffic patterns - events = EventSerializer.load_events_from_json( - "recordings/lite_2minL.json" - ) - ``` - -### Enhanced Documentation System - -=== "Migration" - - - **From**: Sphinx (`.rst` files) - - **To**: MkDocs Material (`.md` files) - - **Benefits**: - - Better performance (8s vs 45s build time) - - Modern UI with tabbed content organization - - Status indicators and cross-references - - Easier maintenance and better code highlighting - - Comprehensive changelog tracking - -=== "Structure" - - ``` - docs/ - ├── marl/ # MARL-specific docs - │ ├── updates.md # Development progress tracking - │ └── changelog/ # Detailed version changelogs - ├── opencda/ # OpenCDA core docs - ├── api/ # API references with tabbed content - └── index.md # MARL-focused homepage - ``` - -## Technical Implementation - -### Complete System Architecture - -=== "File Structure" - - ``` - opencda_marl/ # Main MARL package - ├── coordinator.py # Main orchestrator - ├── core/ # Core components (30 files) - │ ├── adapter/ # OpenCDA integration - │ │ └── vehicle_adapter.py # Vehicle-agent bridge - │ ├── agents/ # Agent implementations (6 files) - │ │ ├── agent_factory.py # Centralized agent creation - │ │ ├── basic_agent.py # Base agent class - │ │ ├── marl_agent.py # MARL agent base - │ │ ├── marl_behavior_agent.py # Enhanced behavior agent - │ │ ├── vanilla_agent.py # Safety-focused agent - │ │ └── rule_based_agent.py # Rule-based intersection agent - │ ├── marl/ # MARL algorithms (5 files) - │ │ └── algorithms/ - │ │ ├── base_algorithm.py # Abstract base class - │ │ ├── q_learning.py # Q-table learning - │ │ ├── dqn.py # Deep Q-Network - │ │ ├── td3.py # Twin Delayed DDPG - │ │ └── smart_replay_buffer.py # Memory management - │ ├── plan/ # Planning components (4 files) - │ ├── safety/ # Safety management (3 files) - │ └── traffic/ # Traffic management system (8 files) - │ ├── traffic_manager.py # Main traffic orchestrator - │ ├── flows.py # Traffic flow configuration - │ ├── events.py # Spawn event system - │ ├── planner.py # Route planning for traffic - │ └── serializer.py # Event recording/replay (JSON) - ├── envs/ # MARL environments (9 files) - │ ├── marl_env.py # Main RL environment - │ ├── carla_monitor.py # CARLA monitoring - │ ├── carla_spectator.py # Camera control - │ ├── evaluation.py # Performance evaluation - │ └── cross_agent_evaluator.py # Multi-agent comparison - ├── gui/ # GUI system (15 files) - │ ├── dashboard.py # Main Qt dashboard - │ ├── observation_viewer.py # Agent state viewer - │ ├── step_controller.py # Simulation control - │ └── widgets/ # GUI panels (12 files) - ├── scenarios/ # Scenario management (4 files) - └── utils/ # Utilities (7 files) - ``` - -=== "Configuration Structure" - - ``` - configs/marl/ # MARL configurations - ├── default.yaml # Base configuration - ├── intersection.yaml # TD3 intersection scenario - ├── intersection_dqn.yaml # DQN configuration - ├── intersection_qbalanced.yaml # Q-Learning balanced - ├── intersection_qcomplex.yaml # Q-Learning complex - ├── intersection_rule_based.yaml # Rule-based agents - ├── intersection_behavior.yaml # Behavior agents - └── intersection_vanilla.yaml # Vanilla agents - ``` - -### Dependencies Added - -| Package | Version | Purpose | -| ----------------- | ------- | ------------------------ | -| `omegaconf` | 2.3.0 | Configuration management | -| `loguru` | 0.7.0 | Enhanced logging | -| `mkdocs-material` | 9.5.3 | Documentation theme | -| `torch` | 2.0+ | Deep learning framework | -| `numpy` | 1.24+ | Numerical computing | -| `PyQt5` | 5.15+ | GUI framework | - -### Configuration Schema - -```yaml -# MARL Traffic Configuration Structure -scenario: - traffic: - mode: "replay" # Traffic mode: record, replay, live - replay_file: "recordings/lite_2minL.json" # Traffic recording file - base_speed: 45.0 # Base vehicle speed (km/h) - active_junctions: [4] # Active junction IDs - - # Traffic flow configuration (for live mode) - flows: - - name: "north" # Flow identifier - rate_vph: 200 # Vehicles per hour per lane - lanes: [0, 1, 2] # Lane indices to use - direction: "north" # Entry direction - start_step: 0 # Start simulation step - end_step: 2400 # End simulation step - speed_variation: 0.2 # Speed variation (±20%) - middle_peak: # Traffic density peak - intensity: 0.4 # Peak intensity multiplier - position: 0.5 # Peak position (0.0-1.0) - width: 0.3 # Peak width (0.0-1.0) - - # Planner configuration for spawn generation - planner: - distance: 5.0 # Base spacing between vehicles (m) - spawn_offset: 7 # Upstream spawn distance multiplier - dest_offset: 7 # Downstream destination multiplier - spawn_z_lift: 0.3 # Z elevation to avoid ground collision (m) - wp_step: 1.0 # Waypoint stepping granularity (m) - allow_uturn: false # Allow U-turn routes -``` - -## API Changes - -### New Classes - -=== "MARLTrafficManager" - - ```python - class MARLTrafficManager: - """Traffic orchestration for MARL scenarios""" - - def __init__(self, world: carla.World, config: dict, state: Dict[str, Any]) - def update(self, current_step: int) -> List[SpawnEvent] - def save_events(self, filepath: str) -> bool - def load_events(self, filepath: str) -> List[SpawnEvent] - - @property - def events(self) -> List[SpawnEvent] - - @property - def total_events(self) -> int - ``` - -=== "TrafficFlow" - - ```python - class TrafficFlow: - """Configuration for directional traffic patterns""" - - def __init__(self, name: str, lanes: List[int], direction: str, - rate_vph: float, start_step: int, end_step: int) - def calc_expected_vehicles(self, fix_dlt: float = 0.05) -> int - def generate_events(self, vbps: List[carla.ActorBlueprint], - planner: MARLPlanner, junction_id: int) -> List[SpawnEvent] - - @property - def duration_steps(self) -> int - ``` - -=== "EventSerializer" - - ```python - class EventSerializer: - """Handles traffic event serialization to/from files""" - - @classmethod - def save_events_to_json(cls, events: List[SpawnEvent], filepath: str) -> bool - - @classmethod - def load_events_from_json(cls, filepath: str) -> List[SpawnEvent] - - @classmethod - def save_events_to_hdf5(cls, events: List[SpawnEvent], filepath: str) -> bool - - @classmethod - def load_events_from_hdf5(cls, filepath: str) -> List[SpawnEvent] - ``` - -=== "Helper Functions" - - ```python - # In opencda_marl.core.traffic.events - class SpawnEvent: - """Represents a single vehicle spawn event""" - spawn_step: int - lane_id: int - route_id: int - target_speed: float - flow_name: str - vehicle_type: str - - # In opencda_marl.core.traffic.planner - class MARLPlanner: - """Route planning for traffic generation""" - def plan_route(self, start_wp: carla.Waypoint, end_wp: carla.Waypoint) -> List[carla.Waypoint] - def get_junction_routes(self, junction_id: int) -> Dict[str, List[carla.Waypoint]] - ``` - -## Usage Examples - -=== "Traffic Replay Mode" - - ```python - import carla - from opencda_marl.core.traffic.traffic_manager import MARLTrafficManager - - # Connect to CARLA - client = carla.Client("localhost", 2000) - world = client.get_world() - - # Setup traffic replay configuration - traffic_config = { - "mode": "replay", - "replay_file": "recordings/lite_2minL.json", - "base_speed": 45.0, - "active_junctions": [4] - } - - # Initialize traffic manager - traffic_manager = MARLTrafficManager(world, traffic_config, {}) - - # Update traffic during simulation - for step in range(2400): # 2 minutes @ 20fps - spawn_events = traffic_manager.update(step) - # Process spawn events... - ``` - -=== "Live Traffic Generation" - - ```python - # Configure live traffic flows - traffic_config = { - "mode": "live", - "base_speed": 45.0, - "active_junctions": [4], - "flows": [ - { - "name": "north", - "rate_vph": 200, - "lanes": [0, 1, 2], - "direction": "north", - "start_step": 0, - "end_step": 2400, - "speed_variation": 0.2, - "middle_peak": { - "intensity": 0.4, - "position": 0.5, - "width": 0.3 - } - } - ] - } - - traffic_manager = MARLTrafficManager(world, traffic_config, {}) - ``` - -=== "Recording Traffic Events" - - ```python - from opencda_marl.core.traffic.serializer import EventSerializer - - # Record traffic events during simulation - all_events = [] - for step in range(2400): - spawn_events = traffic_manager.update(step) - all_events.extend(spawn_events) - - # Save recorded events for replay - EventSerializer.save_events_to_json( - events=all_events, - filepath="recordings/custom_traffic.json", - config=traffic_config - ) - - # Load recorded events - loaded_events = EventSerializer.load_events_from_json( - "recordings/custom_traffic.json" - ) - ``` diff --git a/docs/marl/changelog/v1.0.0.md b/docs/marl/changelog/v1.0.0.md new file mode 100644 index 0000000..61f7acf --- /dev/null +++ b/docs/marl/changelog/v1.0.0.md @@ -0,0 +1,368 @@ +# Version 1.0.0 Changelog + +**Release Date**: January 2026 +**Status**: Stable Release +**Theme**: Complete MARL Framework + +## Major Features + +### MARL Environment System + +=== "Core Environment" + + ```python + # MARLEnv - Custom RL environment with CARLA integration + from opencda_marl.envs.marl_env import MARLEnv + + env = MARLEnv(scenario_manager, config=marl_config) + # Training loop handled by coordinator + ``` + +=== "Features" + + - **Observation System**: Configurable feature extraction via ObservationExtractor + - **Reward Calculation**: Multi-objective rewards (collision, success, progress, safety, speed) + - **Termination Logic**: Episode ending based on collision, completion, or timeout + - **Evaluation Metrics**: Cross-agent performance comparison + - **CARLA Integration**: Direct connection without Gym dependency + - **SUMO Mode**: Lightweight traffic-only simulation via SumoMarlEnv + +### Multi-Agent Framework + +=== "Agent Types" + + ```python + # Agent Factory - Centralized agent creation + from opencda_marl.core.agents.agent_factory import AgentFactory + + # Five implemented agent types + behavior_agent = AgentFactory.create_agent("behavior", config) + vanilla_agent = AgentFactory.create_agent("vanilla", config) + rule_based_agent = AgentFactory.create_agent("rule_based", config) + marl_agent = AgentFactory.create_agent("marl", config) + ``` + +=== "Agent Capabilities" + + - **MARLAgent**: RL-controlled speed, local planner handles steering. Returns (speed, location) + - **Behavior Agent**: Simplified OpenCDA behavior cloning with route following + - **Vanilla Agent**: Enhanced safety with multi-vehicle TTC tracking + - **Rule-based Agent**: 3-stage intersection navigation (junction → following → cruising) + - **Basic Agent**: Full autonomous driving with traffic light and obstacle detection + - **Vehicle Adapters**: Bridge OpenCDA VehicleManager with MARL agent control + +### RL Algorithm Suite + +=== "TD3 (Twin Delayed DDPG)" + + ```yaml + # TD3 - Continuous control with LSTM encoder + MARL: + algorithm: "td3" + state_dim: 8 + action_dim: 1 + td3: + learning_rate_actor: 0.0001 + learning_rate_critic: 0.001 + exploration_noise: 0.3 + noise_decay: 0.998 + min_noise: 0.05 + warmup_steps: 1000 + lstm_hidden: 256 + ``` + + Key features: LSTM multi-agent context encoding, LayerNorm before tanh, delayed policy updates, prioritized experience replay (optional), smart replay buffer with recency bias. + +=== "DQN (Deep Q-Network)" + + ```yaml + # DQN - Discrete speed actions + MARL: + algorithm: "dqn" + state_dim: 7 + dqn: + speed_actions: [0, 5, 8, 12, 15] + learning_rate: 0.001 + memory_size: 50000 + batch_size: 32 + epsilon: 0.1 + epsilon_decay: 0.995 + ``` + +=== "Q-Learning" + + ```yaml + # Q-Learning - Tabular with configurable state bins + MARL: + algorithm: "q_learning" + q_learning: + speed_actions: [15, 35, 65] + state_features: + distance_to_intersection: + bins: [0, 5, 15] + epsilon: 0.1 + learning_rate: 0.2 + ``` + +=== "MAPPO & SAC" + + ```yaml + # MAPPO - Multi-Agent PPO with GAE + MARL: + algorithm: "mappo" + + # SAC - Soft Actor-Critic with entropy regularization + MARL: + algorithm: "sac" + ``` + +### Training Infrastructure + +=== "Algorithm Management" + + ```python + # MARLManager orchestrates the active algorithm + from opencda_marl.core.marl.marl_manager import MARLManager + + manager = MARLManager(config) + action = manager.select_action(observations, ego_id, training=True) + manager.store_transition(obs, ego_id, action, reward, next_obs, done) + losses = manager.update() + ``` + +=== "Checkpoints & Metrics" + + ```python + # CheckpointManager - Structured model saving + from opencda_marl.core.marl.checkpoint import CheckpointManager + + checkpoint_mgr = CheckpointManager(config) + checkpoint_mgr.save(algorithm, episode, reward) # latest + best + per-episode + checkpoint_mgr.load(algorithm, mode="best") # load best model + + # TrainingMetrics - Episode statistics with CSV export + from opencda_marl.core.marl.metrics import TrainingMetrics + + metrics = TrainingMetrics(config) + metrics.update(episode_data) + metrics.export_csv() # Export to metrics_history/ + ``` + +=== "Replay Buffers" + + - **SmartReplayBuffer**: Pre-allocated numpy arrays, O(1) push/sample, recency bias (50% recent + 50% diverse) + - **PrioritizedReplayBuffer**: TD-error weighted sampling, importance sampling with beta annealing + - **RolloutBuffer**: On-policy buffer for MAPPO with GAE computation + +=== "Convergence Detection" + + Automatic convergence detection based on: + + - Coefficient of variation (CV) < 15% over rolling window of 10 episodes + - Success rate stability (CV < 20%) + - Collision rate improving (second half ≤ first half × 1.1) + - Minimum 20 episodes before checking + +### GUI Dashboard System + +=== "Main Dashboard" + + ```python + # PySide6 Qt-based GUI for real-time visualization + from opencda_marl.gui.dashboard import Dashboard + + dashboard = Dashboard(coordinator) + dashboard.show() # Launch interactive GUI + ``` + +=== "GUI Components" + + - **Main Dashboard**: Central control interface with PySide6 Qt widgets + - **Observation Viewer**: Real-time agent state visualization + - **Step Controller**: Manual simulation stepping and episode management + - **Widget Panels**: Agent observation, environment, metrics, reward, system, traffic, weather + +### Traffic Management System + +=== "Core Traffic Manager" + + ```python + # MARLTrafficManager - Orchestrates traffic spawn events + from opencda_marl.core.traffic.traffic_manager import MARLTrafficManager + + traffic_manager = MARLTrafficManager(world, traffic_config, state) + spawn_events = traffic_manager.update(current_step) + ``` + +=== "Traffic Modes" + + - **Record**: Record actual simulation vehicle behavior to JSON/HDF5 + - **Replay**: Replay pre-recorded traffic patterns for reproducibility + - **Live**: Generate traffic on-the-fly using flow configuration + +=== "Event Recording & Replay" + + ```python + from opencda_marl.core.traffic.serializer import EventSerializer + + # Save traffic events + EventSerializer.save_events_to_json(events, "recordings/traffic.json", config) + + # Load for replay + events = EventSerializer.load_events_from_json("recordings/traffic.json") + ``` + +## Technical Implementation + +### Observation System + +The ObservationExtractor supports 9 configurable feature types: + +| Feature | Description | +|---------|-------------| +| `rel_x`, `rel_y` | Relative position to ego | +| `heading` | Vehicle orientation (radians) | +| `speed` | Current velocity | +| `distance_to_intersection` | Remaining distance to junction | +| `distance_to_front` | Distance to nearest vehicle ahead | +| `lane_position` | Lateral position in lane | +| `waypoint_buffer` | Next waypoint distance | +| `min_ttc` | Minimum time-to-collision | +| `distance_to_destination` | Remaining route distance | + +### Reward System + +Multi-objective rewards configurable via YAML: + +| Component | Default Value | Description | +|-----------|--------------|-------------| +| Collision | -500 | Terminal penalty on collision | +| Success | +400 | Terminal reward on reaching destination | +| Step penalty | -0.5 | Per-step cost to encourage efficiency | +| Speed bonus | +1.0 | Reward for maintaining target speed | +| Progress | scaled | Based on distance to destination | +| Stop penalty | -3.0 | Penalty for stopped vehicles | +| Yielding bonus | +1.0 | Reward for yielding to obstacles | + +### TensorBoard Logging + +Comprehensive training metrics logged to TensorBoard: + +- **Losses**: Critic loss, actor loss +- **Q-values**: Q1 mean, Q2 mean +- **Gradients**: Pre-clip norms for critic and actor +- **Exploration**: Noise level over time +- **Learning**: Reward moving average, coefficient of variation +- **Safety**: Near-miss count, TTC violation rate +- **Traffic**: Average speed, speed gap, throughput + +### Dependencies + +| Package | Version | Purpose | +| ----------------- | ------- | ------------------------ | +| `omegaconf` | 2.3+ | Configuration management | +| `loguru` | 0.7+ | Enhanced logging | +| `mkdocs-material` | 9.5+ | Documentation theme | +| `torch` | 2.0+ | Deep learning framework | +| `numpy` | 1.24+ | Numerical computing | +| `pyside6` | 6.0+ | GUI framework | +| `tensorboard` | 2.0+ | Training visualization | + +### Configuration Schema + +```yaml +# Base MARL configuration structure (configs/marl/default.yaml) +meta: + simulator: "carla" # or "sumo" + +world: + sync_mode: true + client_port: 2000 + fixed_delta_seconds: 0.05 + +scenario: + max_steps: 2400 + max_episodes: 500 + traffic: + mode: "replay" + replay_file: "recordings/lite_2minL.json" + base_speed: 45.0 + +MARL: + algorithm: "td3" # td3, dqn, q_learning, mappo, sac + state_dim: 8 + action_dim: 1 + training: true + +agents: + agent_type: "marl" # marl, vanilla, behavior, rule_based + +tensorboard: + enabled: true + log_dir: "runs" + +world_reset: + enabled: true + interval_episodes: 50 +``` + +## API Changes + +### New Classes + +=== "MARLCoordinator" + + ```python + class MARLCoordinator: + """Main MARL orchestrator""" + def __init__(self, config: Dict) + def initialize(self) + def step(self) -> Dict + def run(self) + def reset_episode(self) + def run_gui_mode(self) + def get_metrics(self) -> Dict + def close(self) + ``` + +=== "MARLManager" + + ```python + class MARLManager: + """Algorithm orchestrator""" + def select_action(self, observations, ego_id, training) -> float + def store_transition(self, obs, ego_id, action, reward, next_obs, done) + def update(self) -> Dict[str, float] + def reset_episode(self) + def get_training_info(self) -> Dict + ``` + +=== "BaseAlgorithm" + + ```python + class BaseAlgorithm(ABC): + """Abstract base for all RL algorithms""" + def select_action(self, state, training) -> action + def store_transition(self, state, action, reward, next_state, done) + def update(self) -> Dict[str, float] + def reset_episode(self) + def get_training_info(self) -> Dict + def save(self, path) + def load(self, path) + ``` + +=== "Training Support" + + ```python + class CheckpointManager: + def save(self, algorithm, episode, reward) + def load(self, algorithm, mode="latest") + + class TrainingMetrics: + def update(self, episode_data) + def export_csv(self) + def get_summary(self) -> Dict + + class ObservationExtractor: + def extract(self, vehicle_data) -> np.ndarray + ``` diff --git a/docs/marl/sumo_training.md b/docs/marl/sumo_training.md new file mode 100644 index 0000000..7883d66 --- /dev/null +++ b/docs/marl/sumo_training.md @@ -0,0 +1,385 @@ +# SUMO MARL Training Guide + +!!! warning "Development Status" + SUMO-CARLA co-simulation training is under active development for **v1.1.0**. APIs and configurations may change. + +## Overview + +This guide explains how to use SUMO for accelerated MARL training with transfer learning to CARLA. + +### Training Pipeline + +```mermaid +graph LR + A[SUMO Pre-training] -->|Save checkpoint| B[CARLA Fine-tuning] + B -->|Final policy| C[Evaluation] + + A -.- D["1000 episodes @ 10-80x speed"] + B -.- E["200 episodes with physics"] +``` + +### Performance Benefits + +| Metric | CARLA-only | SUMO → CARLA Transfer | +|--------|------------|----------------------| +| **Training Time** | ~5-7 days | ~1.5 days total | +| **Episodes (1000)** | 168 hours | 12 hours (SUMO) + 24 hours (CARLA) | +| **Agent Scalability** | 10 agents max | 50+ agents in SUMO | +| **GPU Usage** | High | Low (CPU-only SUMO phase) | + +--- + +## Quick Start + +### 1. SUMO Pre-training + +Train a policy in SUMO (10-80x faster than CARLA): + +```bash +# Standard training +pixi run python opencda.py -t sumo --marl + +# With SUMO GUI (visual debugging) +# Edit configs/marl/sumo.yaml: set sumo_gui: true +pixi run python opencda.py -t sumo --marl +``` + +**Training Progress:** + +- Episodes 1-100: Exploration phase (high collision rate) +- Episodes 100-500: Learning phase (collision rate decreasing) +- Episodes 500-1000: Convergence phase (stable policy) + +**Checkpoint Location:** + +- `checkpoints/sumo_td3/latest_checkpoint.pth` +- `checkpoints/sumo_td3/episode_100_checkpoint.pth` +- `checkpoints/sumo_td3/episode_500_checkpoint.pth` + +### 2. CARLA Fine-tuning + +Transfer the SUMO policy to CARLA for physics-accurate fine-tuning. Use any CARLA-based config (e.g., `td3_simple_v4`) with `load_checkpoint` pointing to the SUMO checkpoint: + +```yaml +# In your CARLA config (e.g., configs/marl/td3_simple_v4.yaml) +MARL: + td3: + learning_rate_actor: 5e-4 # Reduced for fine-tuning + exploration_noise: 0.2 # Reduced from 0.5 + + training: + training_mode: true + checkpoint_dir: "checkpoints/carla_finetune_td3/" + load_checkpoint: "checkpoints/sumo_td3/latest_checkpoint.pth" + +scenario: + simulation: + max_episodes: 200 # Fewer episodes needed with transfer +``` + +```bash +# Fine-tune with CARLA +pixi run python opencda.py -t td3_simple_v4 --marl +``` + +!!! tip "Pretrained Mode" + When a checkpoint is loaded, the algorithm automatically skips the warmup phase (`_pretrained=True`), allowing fine-tuning to begin immediately. + +### 3. Evaluation + +Evaluate the fine-tuned policy: + +```yaml +# In your CARLA config +MARL: + training: + training_mode: false # Disable training + load_checkpoint: "checkpoints/carla_finetune_td3/latest_checkpoint.pth" +``` + +```bash +pixi run python opencda.py -t td3_simple_v4 --marl +``` + +--- + +## Configuration + +### SUMO Training Config + +File: `configs/marl/sumo.yaml` + +```yaml +meta: + scenario_type: "intersection_sumo" + simulator: "sumo" + sumo_cfg: "opencda_marl/assets/intersection_sumo/intersection.sumocfg" + +world: + sync_mode: true + fixed_delta_seconds: 0.05 + sumo_port: 8873 + sumo_gui: true # Set to false for headless training + +scenario: + simulation: + max_steps: 2400 + max_episodes: 1000 + +agents: + count: 10 # Can scale to 50+ in SUMO + agent_type: "marl" + +MARL: + algorithm: "td3" + state_dim: 9 + action_dim: 1 + + td3: + features: + rel_x: 1 + rel_y: 1 + position_x: 1 + position_y: 1 + lane_position: 1 + heading_angle: 1 + dist_to_intersection: 1 + dist_to_front_vehicle: 1 + waypoint_buffer: 1 + + exploration_noise: 0.5 # Higher exploration in SUMO + warmup_steps: 500 + + training: + training_mode: true + checkpoint_dir: "checkpoints/sumo_td3/" + save_freq: 10 + load_checkpoint: null # Set to path for resuming + + rewards: + collision: -500.0 + success: 400.0 + step_penalty: -1.5 + speed_bonus: 0.5 +``` + +--- + +## Advanced Usage + +### Scaling Agent Count + +SUMO can handle 50+ agents simultaneously: + +```yaml +# configs/marl/sumo.yaml +agents: + count: 50 +``` + +### Custom SUMO Networks + +1. Place a custom XODR file in `opencda_marl/assets/maps/` +2. Convert to SUMO network: + ```bash + pixi run python scripts/convert_xodr_to_sumo.py + ``` + This generates `.net.xml`, `.rou.xml`, and `.sumocfg` files. +3. Update config: + ```yaml + meta: + sumo_cfg: "opencda_marl/assets/custom_intersection/custom.sumocfg" + ``` + +### Monitoring Training + +Enable SUMO GUI for visual debugging: + +```yaml +# configs/marl/sumo.yaml +world: + sumo_gui: true +``` + +Inspect checkpoint quality: + +```python +import torch + +ckpt = torch.load("checkpoints/sumo_td3/episode_500_checkpoint.pth") +print(f"Episode: {ckpt['episode']}") +print(f"Collision rate: {ckpt['metrics']['collision_rate']}") +print(f"Success rate: {ckpt['metrics']['success_rate']}") +``` + +--- + +## Troubleshooting + +### SUMO Connection Error + +**Error:** +``` +traci.exceptions.TraCIException: Could not connect to TraCI server at localhost:8873 +``` + +**Solution:** + +1. Verify `SUMO_HOME` is set: + ```bash + echo $SUMO_HOME # Should point to SUMO installation + ``` +2. Check port availability: + ```bash + netstat -an | grep 8873 + ``` +3. Change port in config if needed: + ```yaml + world: + sumo_port: 8874 + ``` + +### Transfer Learning Gap + +**Problem:** Policy trained in SUMO performs poorly in CARLA + +**Solutions:** + +1. **Increase fine-tuning episodes:** + ```yaml + scenario: + simulation: + max_episodes: 500 + ``` + +2. **Reduce fine-tuning learning rate further:** + ```yaml + MARL: + td3: + learning_rate_actor: 1e-4 + ``` + +3. **Add domain randomization in SUMO:** + ```yaml + scenario: + traffic: + speed_variation: 0.3 + ``` + +### Out of Memory During CARLA Fine-tuning + +Reduce agent count in the CARLA config: + +```yaml +agents: + count: 5 +``` + +--- + +## Performance Benchmarks + +### Training Time (1000 episodes, 10 agents) + +| Setup | Time | Speedup | +|-------|------|---------| +| CARLA-only (RTX 5090) | ~5-7 days | 1x | +| SUMO-only | ~12 hours | **10-14x** | +| SUMO (900) + CARLA (100) | ~1.5 days | **3-5x** | + +### Memory Usage + +| Setup | GPU VRAM | System RAM | +|-------|----------|-----------| +| CARLA (10 agents) | ~8-12 GB | ~4 GB | +| SUMO (50 agents) | 0 GB | ~2 GB | + +--- + +## Best Practices + +### 1. Observation Space Consistency + +SUMO and CARLA must use identical observation features. The current 9D feature set: + +| Feature | Description | +|---------|-------------| +| `rel_x` | Relative X position to intersection | +| `rel_y` | Relative Y position to intersection | +| `position_x` | Absolute X position | +| `position_y` | Absolute Y position | +| `lane_position` | Lane offset | +| `heading_angle` | Vehicle heading (radians) | +| `dist_to_intersection` | Distance to intersection center | +| `dist_to_front_vehicle` | Gap to leading vehicle | +| `waypoint_buffer` | Waypoint-based path feature | + +!!! danger "Important" + Do NOT modify feature extraction in SUMO without updating the CARLA config to match. + +### 2. Reward Structure + +Keep rewards identical between SUMO and CARLA: + +```yaml +rewards: + collision: -500.0 + success: 400.0 + step_penalty: -1.5 + speed_bonus: 0.5 +``` + +### 3. Hyperparameter Tuning + +Only tune these during fine-tuning: + +- `learning_rate_actor` / `learning_rate_critic` +- `exploration_noise` +- `warmup_steps` + +Keep these **fixed** (must match SUMO): + +- `state_dim` / `action_dim` +- Network architecture (`conflict_encoder`, `motion_planner`) +- `discount`, `tau`, etc. + +### 4. Checkpoint Management + +Save frequently in SUMO (fast and cheap): + +```yaml +training: + save_freq: 10 # Every 10 episodes +``` + +Save less frequently in CARLA (resource intensive): + +```yaml +training: + save_freq: 5 +``` + +--- + +## Architecture + +### SUMO Adapter Layer + +The SUMO integration provides CARLA-compatible interfaces for seamless policy transfer: + +| Component | File | Description | +|-----------|------|-------------| +| **SumoMARLEnv** | `opencda_marl/envs/sumo_marl_env.py` | SUMO-only training environment | +| **SumoAdapter** | `opencda_marl/core/traffic/sumo_adapter.py` | CARLA-compatible waypoint/map wrappers | +| **SumoSpawner** | `opencda_marl/core/traffic/sumo_spawner.py` | Vehicle spawning via TraCI | +| **XODR Converter** | `scripts/convert_xodr_to_sumo.py` | OpenDRIVE → SUMO network converter | + +The adapter layer converts between SUMO and CARLA coordinate systems (offset: `99.8, 100.0`) and provides compatible `SumoWaypoint`, `SumoJunction`, `SumoWorld`, and `SumoMap` classes. + +--- + +## References + +- [SUMO Documentation](https://sumo.dlr.de/docs/) +- [TraCI API Reference](https://sumo.dlr.de/docs/TraCI.html) +- [OpenCDA Documentation](https://opencda-documentation.readthedocs.io/en/latest/) diff --git a/docs/marl/updates.md b/docs/marl/updates.md index bb3f023..ce0a42d 100644 --- a/docs/marl/updates.md +++ b/docs/marl/updates.md @@ -3,8 +3,8 @@ This page tracks the development progress of the OpenCDA-MARL extension, documenting all modifications, features, and improvements across versions. -!!! info "Version Tracking" - The MARL extension follows semantic versioning (MAJOR.MINOR.PATCH)aligned with OpenCDA releases. Current version: **0.1.0-alpha** (Initial Development) +!!! success "Version Tracking" + Current stable release: **v1.0.0** — Complete MARL framework with five RL algorithms, training infrastructure, and evaluation tools. ## Release Timeline @@ -13,33 +13,77 @@ gantt title MARL Development Timeline dateFormat YYYY-MM-DD section Framework - v0.1.0-alpha (Foundation) :done, 2025-08-07, 35d - v0.1.0-beta (MARL Algorithms) :active, 2025-09-15, 30d + Foundation & Adapters :done, 2025-08-07, 35d + RL Algorithms & Training :done, 2025-09-15, 75d + Stabilization & Documentation :done, 2025-12-01, 60d + section Releases + v1.0.0 Stable Release :milestone, 2026-01-30, 0d + section Planned + v1.1.0 (Advanced Features) :2026-02-01, 60d ``` -| Version | Release Date | Status | Highlights | -| ----------- | ------------ | ------------ | -------------------------- | -| 0.1.0-alpha | 2025-08-07 | ✅Current | Foundation & Documentation | -| 0.1.0-beta | 2025-09-15 | 🚧Development | MARL algorithms | +| Version | Release Date | Status | Highlights | +| ------- | ------------ | ------- | ------------------------------------------------------- | +| 1.0.0 | 2026-01-30 | Stable | Complete MARL framework, 5 algorithms, training, GUI | +| 1.1.0 | 2026 Q1 | Planned | Advanced features & optimizations | -## Current Development +## v1.0.0 (Stable Release) !!! warning "No Breaking Changes" The MARL extension is designed to be non-invasive. All OpenCDA functionality remains unchanged. -### v0.1.0-alpha (Foundation) +The first stable release consolidates all development phases into a complete, production-ready MARL framework. -OpenCDA-MARL v0.1.0-alpha establishes the foundational Multi-Agent Reinforcement Learning framework with implementing a comprehensive 3-layer architecture. The system provides four distinct agent types including behavior, vanilla, rule-based, and MARL agents managed through a centralized agent factory pattern. Core RL algorithms are implemented with Q-Learning (discrete state/action spaces), DQN (deep neural network approximation), and TD3 (continuous control) providing diverse learning approaches for intersection scenarios. +### Core Framework -The MARL environment system features custom CARLA integration with observation extraction, multi-objective reward calculation, and cross-agent evaluation capabilities. A Qt-based GUI dashboard enables real-time visualization, manual simulation control, and agent observation monitoring. Vehicle adapters serve as the critical bridge layer between OpenCDA's proven autonomous driving stack and MARL control systems, preserving all original OpenCDA functionality while enabling RL-based decision making. +OpenCDA-MARL provides a comprehensive 3-layer architecture for multi-agent reinforcement learning in autonomous driving. The system includes four agent types (behavior, vanilla, rule-based, MARL) managed through a centralized factory pattern. Vehicle adapters bridge OpenCDA's autonomous driving stack with MARL control systems. -The implementation focuses on intersection scenarios with custom XODR maps and traffic replay patterns, establishing a solid foundation for multi-agent research in cooperative autonomous driving. Registry-based map management provides predictable loading behavior with support for custom intersection environments and automated spawn point generation based on junction analysis. +The MARLEnv environment provides custom CARLA integration with observation extraction, multi-objective reward calculation, and cross-agent evaluation. A PySide6 Qt-based GUI dashboard enables real-time visualization and interactive control. + +### RL Algorithm Suite + +=== "Algorithms" + + Five RL algorithms with a shared `BaseAlgorithm` interface: + + | Algorithm | Type | Key Features | + |-----------|------|-------------| + | **TD3** | Continuous | LSTM encoder, multi-agent context, prioritized replay | + | **DQN** | Discrete | Epsilon-greedy, target network, gradient clipping | + | **Q-Learning** | Discrete | Tabular, configurable state bins | + | **MAPPO** | On-Policy | GAE, rollout buffer, Gaussian actor | + | **SAC** | Continuous | Entropy regularization, auto-tuning alpha | + +=== "Training Infrastructure" + + - **MARLManager**: Algorithm orchestrator with training/evaluation modes + - **ObservationExtractor**: 9 configurable feature types for RL observation vectors + - **CheckpointManager**: Structured model saving (latest, best, per-episode) + - **TrainingMetrics**: Per-episode statistics with CSV export + - **SmartReplayBuffer**: Pre-allocated numpy arrays with recency bias (50% recent + 50% diverse) + - **PrioritizedReplayBuffer**: TD-error weighted experience sampling + - **TensorBoard**: Comprehensive logging (losses, Q-values, gradients, convergence) + - **Convergence Detection**: CV-based (< 15%), success rate stability, min 20 episodes + +=== "SUMO Integration" + + - **SumoMarlEnv**: Lightweight SUMO-only environment for traffic simulation + - **SUMO adapter/spawner**: Vehicle spawning and control in SUMO + - Enables large-scale experiments without CARLA overhead + +## Planned: v1.1.0 + +- Advanced multi-agent communication protocols +- Curriculum learning for progressive scenario difficulty +- Distributed training support +- Highway and parking scenario templates +- Performance benchmarking across algorithms ## Changelog Template When adding changelog entries: -1. **Version File**: Create/update version-specific file (e.g., `v0.2.0.md`) +1. **Version File**: Create/update version-specific file (e.g., `v1.1.0.md`) 2. **Categories**: Use consistent categories (Architecture, APIs, Config, etc.) 3. **Impact**: Note breaking changes and migration requirements 4. **Examples**: Include code examples for significant changes @@ -55,31 +99,31 @@ When adding changelog entries: ```markdown # Version X.Y.Z Changelog -**Release Date**: YYYY-MM -**Status**: Current/Development/Planned +**Release Date**: YYYY-MM +**Status**: Current/Development/Planned **Theme**: Brief description -## 🎯 Major Features +## Major Features [Feature descriptions with code examples] -## 🔧 Technical Details +## Technical Details [Implementation details] -## 🐛 Bug Fixes +## Bug Fixes [Fixed issues] -## 📊 Performance +## Performance [Performance improvements] -## 🚧 Known Limitations +## Known Limitations [Current limitations] -## 📝 API Changes +## API Changes [New/modified APIs] -## 🔄 Migration Notes +## Migration Notes [Migration instructions] ``` !!! tip "Stay Updated" - Watch the [GitHub repository](https://github.com/radar-lab/OpenCDA-MARL) for the latest updates and releases. + Watch the [GitHub repository](https://github.com/radar-lab/opencda-marl) for the latest updates and releases. diff --git a/docs/quick-start.md b/docs/quick-start.md index 76696f6..1d45571 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -213,7 +213,7 @@ pixi run start -t [--apply_ml] [--record] **Tips**: -- Check `configs/opencda/scenario_testing/config_yaml/` for configuration examples +- Check `configs/opencda/{config_yaml}.yaml` for configuration examples - Use `opencda/customize/` for custom implementations - See [YAML Configuration Guide](opencda/yaml_define.md) for all options @@ -223,8 +223,6 @@ pixi run start -t [--apply_ml] [--record] **What it provides**: Multi-Agent Reinforcement Learning capabilities for cooperative driving research -!!! info "Development Status" - OpenCDA-MARL is currently in active development. Current version: **0.1.0-alpha**. See [MARL Architecture](marl/architecture.md) for detailed development status. ### Quick Test @@ -308,7 +306,7 @@ pixi run marl-quick-test-gui === "Q-Learning" ```bash # Q-table based learning (balanced configuration) - pixi run marl-quick-test -t intersection_qbalanced + pixi run marl-quick-test -t qbalanced ``` **Configuration**: @@ -332,7 +330,7 @@ pixi run marl-quick-test-gui === "Deep Q-Network (DQN)" ```bash # Neural network Q-learning - pixi run marl-quick-test -t intersection_dqn + pixi run marl-quick-test -t dqn ``` **Configuration**: @@ -350,10 +348,10 @@ pixi run marl-quick-test-gui batch_size: 32 ``` -=== "TD3 (Twin Delayed DDPG)" +=== "MATD3 (Twin Delayed DDPG)" ```bash # Continuous control with TD3 - pixi run marl-quick-test -t intersection_td3 + pixi run marl-quick-test -t td3_simple ``` **Configuration**: @@ -371,7 +369,44 @@ pixi run marl-quick-test-gui exploration_noise: 0.5 ``` ---- +=== "MASAC (Soft Actor Critic)" + ```bash + # Soft Actor Critic with auto-tuning entropy + pixi run marl-quick-test -t sac + ``` + + **Configuration**: + ```yaml + agents: + agent_type: "marl" + marl: + algorithm: "sac" + sac: + learning_rate_actor: 0.001 + learning_rate_critic: 0.001 + learning_rate_alpha: 0.001 + auto_entropy_tuning: true + target_entropy: -1.0 + init_alpha: 0.2 + ``` + +=== "MAPPO (Multi-Agent PPO)" + ```bash + # Multi-Agent PPO with CTDE + pixi run marl-quick-test -t mappo + ``` + + **Configuration**: + ```yaml + agents: + agent_type: "marl" + marl: + algorithm: "mappo" + mappo: + learning_rate: 0.001 + tau: 0.005 + alpha: 0.2 + ``` ### Configuration & Customization @@ -416,7 +451,7 @@ pixi run marl-quick-test-gui # Fine-tune RL algorithm parameters agents: agent_type: "marl" - + MARL: algorithm: "dqn" dqn: diff --git a/mkdocs.yml b/mkdocs.yml index cb921f2..4351ff5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -106,11 +106,12 @@ nav: - FAQ: faq.md - MARL Framework: - Architecture: marl/architecture.md + - SUMO Training: marl/sumo_training.md #- Algorithms: marl/algorithms.md #- Training: marl/training.md - Updates: marl/updates.md - Changelog-MARL: - - v0.1.0-alpha: marl/changelog/v0.1.0-alpha.md + - v1.0.0: marl/changelog/v1.0.0.md - Contributing: contributing.md - OpenCDA Integration: - Core Components: opencda/core.md @@ -140,9 +141,7 @@ nav: - Overview: api/opencda-marl/overview.md - Coordinator: api/opencda-marl/coordinator.md - Scenario: api/opencda-marl/scenario.md - - Adapters: - - Map Adapter: api/opencda-marl/adapters/map_adapter.md - - Vehicle Adapter: api/opencda-marl/adapters/vehicle_adapter.md + - Vehicle Adapter: api/opencda-marl/adapters/vehicle_adapter.md - Core Components: - Agent Manager: api/opencda-marl/agent_manager.md - Map Manager: api/opencda-marl/map_manager.md @@ -153,7 +152,7 @@ nav: - About Us: about.md extra: - homepage: https://lgcyaxi.github.io/opencda-marl + homepage: https://github.com/radar-lab/opencda-marl social: - icon: fontawesome/brands/github link: https://github.com/radar-lab/opencda-marl @@ -162,10 +161,10 @@ extra: link: https://opencda-documentation.readthedocs.io/en/latest/ name: OpenCDA Documentation - icon: fontawesome/solid/university - link: https://github.com/radar-lab + link: https://radar.ece.arizona.edu/ name: UA Radar Lab - version: - provider: mike + # version: + # provider: mike # Enable when using mike for versioned docs deployment analytics: feedback: title: Was this page helpful? diff --git a/opencda/co_simulation/sumo_integration/bridge_helper.py b/opencda/co_simulation/sumo_integration/bridge_helper.py index d696c04..fcc1ce0 100644 --- a/opencda/co_simulation/sumo_integration/bridge_helper.py +++ b/opencda/co_simulation/sumo_integration/bridge_helper.py @@ -122,15 +122,26 @@ def get_carla_blueprint(sumo_actor, sync_color=False): blueprint_library = BridgeHelper.blueprint_library type_id = sumo_actor.type_id + # Known SUMO default types that are expected to be mapped + SUMO_DEFAULT_TYPES = {'DEFAULT_VEHTYPE', 'DEFAULT_BIKETYPE', 'DEFAULT_PEDTYPE', + 'DEFAULT_WHEELED_VEHICLE', 'DEFAULT_2_WHEELED_VEHICLE'} + if type_id in [bp.id for bp in blueprint_library]: blueprint = blueprint_library.filter(type_id)[0] logging.debug('[BridgeHelper] sumo vtype %s found in carla blueprints', type_id) else: blueprint = BridgeHelper._get_recommended_carla_blueprint(sumo_actor) if blueprint is not None: - logging.warning( - 'sumo vtype %s not found in carla. The following blueprint will be used: %s', - type_id, blueprint.id) + # Only warn for non-default SUMO types (custom types that might be typos) + if type_id not in SUMO_DEFAULT_TYPES: + logging.warning( + 'sumo vtype %s not found in carla. The following blueprint will be used: %s', + type_id, blueprint.id) + else: + # Debug-level logging for expected SUMO default types + logging.debug( + '[BridgeHelper] sumo default vtype %s mapped to carla blueprint: %s', + type_id, blueprint.id) else: logging.error('sumo vtype %s not supported. No vehicle will be spawned in carla', type_id) diff --git a/opencda/co_simulation/sumo_integration/sumo_simulation.py b/opencda/co_simulation/sumo_integration/sumo_simulation.py index f96ba74..99cf4e6 100644 --- a/opencda/co_simulation/sumo_integration/sumo_simulation.py +++ b/opencda/co_simulation/sumo_integration/sumo_simulation.py @@ -294,14 +294,14 @@ def _get_sumo_net(cfg_file): cfg_file = os.path.join(os.getcwd(), cfg_file) tree = ET.parse(cfg_file) - tag = tree.find('//net-file') + tag = tree.find('.//net-file') # Fixed FutureWarning if tag is None: return None net_file = os.path.join(os.path.dirname(cfg_file), tag.get('value')) logging.debug('Reading net file: %s', net_file) - sumo_net = traci.sumolib.net.readNet(net_file) + sumo_net = sumolib.net.readNet(net_file) # Fixed: sumolib is imported directly return sumo_net class SumoSimulation(object): @@ -317,13 +317,21 @@ def __init__(self, cfg_file, step_length, host=None, port=None, sumo_gui=False, if host is None or port is None: logging.info('Starting new sumo server...') if sumo_gui is True: - logging.info('Remember to press the play button to start the simulation') + logging.info('SUMO GUI will auto-start (non-blocking)') - traci.start([sumo_binary, + # Build command arguments + cmd_args = [ + sumo_binary, '--configuration-file', cfg_file, '--step-length', str(step_length), '--collision.check-junctions' - ]) + ] + + # Add auto-start flag for GUI to prevent blocking + if sumo_gui is True: + cmd_args.append('--start') + + traci.start(cmd_args) else: logging.info('Connection to sumo server. Host: %s Port: %s', host, port) diff --git a/opencda/co_simulation/sumo_integration/vtypes.json b/opencda/co_simulation/sumo_integration/vtypes.json index 6bb1436..845a120 100644 --- a/opencda/co_simulation/sumo_integration/vtypes.json +++ b/opencda/co_simulation/sumo_integration/vtypes.json @@ -5,7 +5,13 @@ "DEFAULT_WHEELED_VEHICLE": { "vClass": "passenger" }, + "DEFAULT_VEHTYPE": { + "vClass": "passenger" + }, "carla_blueprints": { + "DEFAULT_VEHTYPE": { + "vClass": "passenger" + }, "vehicle.audi.a2": { "vClass": "passenger" }, diff --git a/opencda/core/actuation/pid_controller.py b/opencda/core/actuation/pid_controller.py index 274df9a..f3af975 100644 --- a/opencda/core/actuation/pid_controller.py +++ b/opencda/core/actuation/pid_controller.py @@ -121,18 +121,20 @@ def lon_run_step(self, target_speed): """ error = target_speed - self.current_speed - self._lat_ebuffer.append(error) + # FIXED: Use _lon_ebuffer instead of _lat_ebuffer for longitudinal control + self._lon_ebuffer.append(error) - if len(self._lat_ebuffer) >= 2: - _de = (self._lat_ebuffer[-1] - self._lat_ebuffer[-2]) / self.dt - _ie = sum(self._lat_ebuffer) * self.dt + if len(self._lon_ebuffer) >= 2: + _de = (self._lon_ebuffer[-1] - self._lon_ebuffer[-2]) / self.dt + _ie = sum(self._lon_ebuffer) * self.dt else: _de = 0.0 _ie = 0.0 - return np.clip((self._lat_k_p * error) + - (self._lat_k_d * _de) + - (self._lat_k_i * _ie), + # FIXED: Use _lon_k_* gains instead of _lat_k_* for longitudinal control + return np.clip((self._lon_k_p * error) + + (self._lon_k_d * _de) + + (self._lon_k_i * _ie), -1.0, 1.0) def lat_run_step(self, target_location): @@ -170,10 +172,11 @@ def lat_run_step(self, target_location): if _cross[2] < 0: _dot *= -1.0 - self._lon_ebuffer.append(_dot) - if len(self._lon_ebuffer) >= 2: - _de = (self._lon_ebuffer[-1] - self._lon_ebuffer[-2]) / self.dt - _ie = sum(self._lon_ebuffer) * self.dt + # FIXED: Use _lat_ebuffer instead of _lon_ebuffer for lateral control + self._lat_ebuffer.append(_dot) + if len(self._lat_ebuffer) >= 2: + _de = (self._lat_ebuffer[-1] - self._lat_ebuffer[-2]) / self.dt + _ie = sum(self._lat_ebuffer) * self.dt else: _de = 0.0 _ie = 0.0 diff --git a/opencda/core/safety/safety_manager.py b/opencda/core/safety/safety_manager.py index d5bc9d7..25c10d7 100644 --- a/opencda/core/safety/safety_manager.py +++ b/opencda/core/safety/safety_manager.py @@ -55,3 +55,6 @@ def update_info(self, data_dict) -> dict: def destroy(self): for sensor in self.sensors: sensor.destroy() + self.sensors.clear() # Clear references to avoid GC warnings + self.imu_sensor = None + self.vehicle = None diff --git a/opencda/core/safety/sensors.py b/opencda/core/safety/sensors.py index 497a25f..1a4b76c 100644 --- a/opencda/core/safety/sensors.py +++ b/opencda/core/safety/sensors.py @@ -73,12 +73,9 @@ def destroy(self) -> None: Clear collision sensor in Carla world. """ self._history.clear() - try: - if hasattr(self, 'sensor') and self.sensor and self.sensor.is_alive: - self.sensor.stop() - self.sensor.destroy() - except Exception as e: - print(f"Warning: Failed to destroy collision sensor: {e}") + if hasattr(self, 'sensor') and self.sensor and self.sensor.is_alive: + self.sensor.stop() + self.sensor.destroy() class IMUSensor(object): @@ -124,12 +121,9 @@ def tick(self, data_dict): pass def destroy(self) -> None: - try: - if hasattr(self, 'sensor') and self.sensor and self.sensor.is_alive: - self.sensor.stop() - self.sensor.destroy() - except Exception as e: - print(f"Warning: Failed to destroy IMU sensor: {e}") + if hasattr(self, 'sensor') and self.sensor and self.sensor.is_alive: + self.sensor.stop() + self.sensor.destroy() class StuckDetector(object): diff --git a/opencda/core/sensing/localization/localization_manager.py b/opencda/core/sensing/localization/localization_manager.py index ad1ee97..d35544c 100644 --- a/opencda/core/sensing/localization/localization_manager.py +++ b/opencda/core/sensing/localization/localization_manager.py @@ -80,15 +80,12 @@ def _on_gnss_event(weak_self, event): self.lon = event.longitude self.alt = event.altitude self.timestamp = event.timestamp - + def destroy(self): """Destroy the GNSS sensor.""" - try: - if hasattr(self, 'sensor') and self.sensor and self.sensor.is_alive: - self.sensor.stop() - self.sensor.destroy() - except Exception as e: - print(f"Warning: Failed to destroy GNSS sensor: {e}") + if hasattr(self, 'sensor') and self.sensor and self.sensor.is_alive: + self.sensor.stop() + self.sensor.destroy() class ImuSensor(object): @@ -146,15 +143,12 @@ def _IMU_callback(weak_self, sensor_data): max(limits[0], min(limits[1], sensor_data.gyroscope.y)), max(limits[0], min(limits[1], sensor_data.gyroscope.z))) self.compass = sensor_data.compass - + def destroy(self): """Destroy the IMU sensor.""" - try: - if hasattr(self, 'sensor') and self.sensor and self.sensor.is_alive: - self.sensor.stop() - self.sensor.destroy() - except Exception as e: - print(f"Warning: Failed to destroy IMU sensor: {e}") + if hasattr(self, 'sensor') and self.sensor and self.sensor.is_alive: + self.sensor.stop() + self.sensor.destroy() class LocalizationManager(object): @@ -360,11 +354,13 @@ def destroy(self): try: if hasattr(self, 'gnss') and self.gnss: self.gnss.destroy() + self.gnss = None except Exception as e: - print(f"Warning: Failed to destroy GNSS sensor: {e}") - + pass # Suppress warning - sensor may already be destroyed + try: if hasattr(self, 'imu') and self.imu: self.imu.destroy() + self.imu = None except Exception as e: - print(f"Warning: Failed to destroy IMU sensor: {e}") + pass # Suppress warning - sensor may already be destroyed diff --git a/opencda/core/sensing/perception/perception_manager.py b/opencda/core/sensing/perception/perception_manager.py index 8ab3707..d31c601 100644 --- a/opencda/core/sensing/perception/perception_manager.py +++ b/opencda/core/sensing/perception/perception_manager.py @@ -928,41 +928,24 @@ def destroy(self): """ if self.rgb_camera: for rgb_camera in self.rgb_camera: - try: - if hasattr(rgb_camera, 'sensor') and rgb_camera.sensor.is_alive: - rgb_camera.sensor.stop() - rgb_camera.sensor.destroy() - except Exception as e: - print(f"Warning: Failed to destroy RGB camera sensor: {e}") + if hasattr(rgb_camera, 'sensor') and rgb_camera.sensor.is_alive: + rgb_camera.sensor.stop() + rgb_camera.sensor.destroy() if self.lidar: - try: - if hasattr(self.lidar, 'sensor') and self.lidar.sensor.is_alive: - self.lidar.sensor.stop() - self.lidar.sensor.destroy() - except Exception as e: - print(f"Warning: Failed to destroy LiDAR sensor: {e}") + if hasattr(self.lidar, 'sensor') and self.lidar.sensor.is_alive: + self.lidar.sensor.stop() + self.lidar.sensor.destroy() if self.camera_visualize: - try: - cv2.destroyAllWindows() - except Exception as e: - print(f"Warning: Failed to destroy camera windows: {e}") + cv2.destroyAllWindows() # Only destroy if visualizer was actually initialized # This prevents errors when lazy initialization never occurred if self.lidar_visualize and self.o3d_vis_initialized and self.o3d_vis: - try: - self.o3d_vis.destroy_window() - except Exception as e: - print(f"Warning: Failed to destroy LiDAR window: {e}") - elif self.lidar_visualize and not self.o3d_vis_initialized: - print("Info: LiDAR visualizer was never initialized, no window to destroy") + self.o3d_vis.destroy_window() if self.data_dump: - try: - if hasattr(self, 'semantic_lidar') and hasattr(self.semantic_lidar, 'sensor') and self.semantic_lidar.sensor.is_alive: - self.semantic_lidar.sensor.stop() - self.semantic_lidar.sensor.destroy() - except Exception as e: - print(f"Warning: Failed to destroy semantic LiDAR sensor: {e}") + if hasattr(self, 'semantic_lidar') and hasattr(self.semantic_lidar, 'sensor') and self.semantic_lidar.sensor.is_alive: + self.semantic_lidar.sensor.stop() + self.semantic_lidar.sensor.destroy() diff --git a/opencda/scenario_testing/platoon_joining_2lanefree_cosim.py b/opencda/scenario_testing/platoon_joining_2lanefree_cosim.py index a06841d..e9910bc 100644 --- a/opencda/scenario_testing/platoon_joining_2lanefree_cosim.py +++ b/opencda/scenario_testing/platoon_joining_2lanefree_cosim.py @@ -19,6 +19,11 @@ def run_scenario(opt, scenario_params): + scenario_manager = None + eval_manager = None + platoon_list = [] + single_cav_list = [] + try: scenario_params = add_current_time(scenario_params) @@ -37,6 +42,7 @@ def run_scenario(opt, scenario_params): scenario_manager = \ sim_api.CoScenarioManager(scenario_params, opt.apply_ml, + carla_version='0.9.15', xodr_path=xodr_path, cav_world=cav_world, sumo_file_parent_path=sumo_cfg) @@ -83,8 +89,11 @@ def run_scenario(opt, scenario_params): single_cav.vehicle.apply_control(control) finally: - eval_manager.evaluate() - scenario_manager.close() + if eval_manager is not None: + eval_manager.evaluate() + + if scenario_manager is not None: + scenario_manager.close() for platoon in platoon_list: platoon.destroy() diff --git a/opencda/scenario_testing/single_2lanefree_cosim.py b/opencda/scenario_testing/single_2lanefree_cosim.py index 5b87c84..0dd0c94 100644 --- a/opencda/scenario_testing/single_2lanefree_cosim.py +++ b/opencda/scenario_testing/single_2lanefree_cosim.py @@ -20,6 +20,10 @@ def run_scenario(opt, scenario_params): + scenario_manager = None + eval_manager = None + single_cav_list = [] + try: scenario_params = add_current_time(scenario_params) @@ -38,6 +42,7 @@ def run_scenario(opt, scenario_params): scenario_manager = \ sim_api.CoScenarioManager(scenario_params, opt.apply_ml, + carla_version='0.9.15', xodr_path=xodr_path, cav_world=cav_world, sumo_file_parent_path=sumo_cfg) @@ -69,7 +74,11 @@ def run_scenario(opt, scenario_params): single_cav.vehicle.apply_control(control) finally: - eval_manager.evaluate() - scenario_manager.close() + if eval_manager is not None: + eval_manager.evaluate() + + if scenario_manager is not None: + scenario_manager.close() + for v in single_cav_list: v.destroy() diff --git a/opencda/scenario_testing/single_town05_cosim.py b/opencda/scenario_testing/single_town05_cosim.py index b24130d..e1d710c 100644 --- a/opencda/scenario_testing/single_town05_cosim.py +++ b/opencda/scenario_testing/single_town05_cosim.py @@ -15,6 +15,10 @@ def run_scenario(opt, scenario_params): + scenario_manager = None + eval_manager = None + single_cav_list = [] + try: scenario_params = add_current_time(scenario_params) @@ -30,6 +34,7 @@ def run_scenario(opt, scenario_params): scenario_manager = \ sim_api.CoScenarioManager(scenario_params, opt.apply_ml, + carla_version='0.9.15', town='Town05', cav_world=cav_world, sumo_file_parent_path=sumo_cfg) @@ -61,7 +66,11 @@ def run_scenario(opt, scenario_params): single_cav.vehicle.apply_control(control) finally: - eval_manager.evaluate() - scenario_manager.close() + if eval_manager is not None: + eval_manager.evaluate() + + if scenario_manager is not None: + scenario_manager.close() + for v in single_cav_list: v.destroy() diff --git a/opencda/scenario_testing/single_town06_cosim.py b/opencda/scenario_testing/single_town06_cosim.py index a975c0f..5dc3604 100644 --- a/opencda/scenario_testing/single_town06_cosim.py +++ b/opencda/scenario_testing/single_town06_cosim.py @@ -15,6 +15,10 @@ def run_scenario(opt, scenario_params): + scenario_manager = None + eval_manager = None + single_cav_list = [] + try: scenario_params = add_current_time(scenario_params) @@ -30,6 +34,7 @@ def run_scenario(opt, scenario_params): scenario_manager = \ sim_api.CoScenarioManager(scenario_params, opt.apply_ml, + carla_version='0.9.15', town='Town06', cav_world=cav_world, sumo_file_parent_path=sumo_cfg) @@ -61,7 +66,11 @@ def run_scenario(opt, scenario_params): single_cav.vehicle.apply_control(control) finally: - eval_manager.evaluate() - scenario_manager.close() + if eval_manager is not None: + eval_manager.evaluate() + + if scenario_manager is not None: + scenario_manager.close() + for v in single_cav_list: v.destroy() diff --git a/opencda_marl/assets/intersection_sumo/README.md b/opencda_marl/assets/intersection_sumo/README.md new file mode 100644 index 0000000..ced5ce8 --- /dev/null +++ b/opencda_marl/assets/intersection_sumo/README.md @@ -0,0 +1,38 @@ +# SUMO Intersection Assets (MARL) + +This directory contains SUMO network files converted from OpenDRIVE format specifically for MARL training. + +**Location:** `opencda_marl/assets/intersection_sumo/` + +This is separate from OpenCDA's co-simulation assets because it's optimized for pure SUMO MARL training without CARLA. + +## Files + +- `intersection.net.xml` - SUMO network file (converted from XODR) +- `intersection.rou.xml` - Route definitions for intersection scenario +- `intersection.sumocfg` - SUMO configuration file + +## Usage + +These files are automatically loaded by `SumoMARLEnv` when using SUMO-only MARL training. + +Configuration in `configs/marl/intersection_sumo.yaml`: +```yaml +meta: + scenario_type: "intersection_sumo" + sumo_cfg: "opencda_marl/assets/intersection_sumo/intersection.sumocfg" +``` + +## Manual Testing + +To test the SUMO network manually: +```bash +sumo-gui -c intersection.sumocfg +``` + +## Regeneration + +To regenerate these files from the XODR source: +```bash +python scripts/convert_xodr_to_sumo.py +``` diff --git a/opencda_marl/assets/intersection_sumo/intersection.net.xml b/opencda_marl/assets/intersection_sumo/intersection.net.xml new file mode 100644 index 0000000..543e201 --- /dev/null +++ b/opencda_marl/assets/intersection_sumo/intersection.net.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/opencda_marl/assets/intersection_sumo/intersection.rou.xml b/opencda_marl/assets/intersection_sumo/intersection.rou.xml new file mode 100644 index 0000000..349d3da --- /dev/null +++ b/opencda_marl/assets/intersection_sumo/intersection.rou.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/opencda_marl/assets/intersection_sumo/intersection.sumocfg b/opencda_marl/assets/intersection_sumo/intersection.sumocfg new file mode 100644 index 0000000..0a58b97 --- /dev/null +++ b/opencda_marl/assets/intersection_sumo/intersection.sumocfg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/opencda_marl/coordinator.py b/opencda_marl/coordinator.py index 10b446c..f1857a5 100644 --- a/opencda_marl/coordinator.py +++ b/opencda_marl/coordinator.py @@ -11,7 +11,7 @@ import traceback from opencda.core.common.cav_world import CavWorld -from opencda_marl.envs import CarlaMonitor, CarlaSpectator, MARLEnv, EvaluationManager +from opencda_marl.envs import CarlaMonitor, CarlaSpectator, MARLEnv, SumoMARLEnv, EvaluationManager from opencda_marl.scenarios import ScenarioBuilder @@ -40,6 +40,7 @@ def __init__( self.carla_client = None self.carla_world = None self.carla_monitor = None + self.world_reset_manager = None # Callbacks for external control (GUI, etc.) self.pre_step_callbacks: List[Callable] = [] @@ -55,51 +56,119 @@ def initialize(self): 2. Monitor the CARLA simulation? """ - # 1. Create CAV world - self.cav_world = CavWorld(apply_ml=self.config.opt.apply_ml) - - # 2. Create Scenario Manager - self.scenario_manager = ScenarioBuilder.build_from_config( - config=self.config, - cav_world=self.cav_world, - ) - self.states = self.scenario_manager.states - self.carla_client = self.scenario_manager.client - self.carla_world = self.scenario_manager.world - - # 3. Create Spectator for GUI - spectator_config = self.config.get('spectator', {}) - self.carla_spectator = CarlaSpectator( - self.carla_world, spectator_config) - - # 4. Create MARL environment - Marl_cfg = self.config.get('MARL', {}) - self.marl_env = MARLEnv(self.scenario_manager, - config=Marl_cfg) - - # 5. Create CARLA monitor - marl_tm = self.scenario_manager.traffic_manager - self.carla_monitor = CarlaMonitor(self.carla_world, - marl_tm=marl_tm) - - # 6. Create Evaluation manager - scenario_type = self.config.get("meta", {}).get("scenario_type", None) - agent_name = self.config.get("agents", {}).get("agent_type", None) - eval_cfg = self.config.get('evaluation', {}) - self.evaluation_manager = EvaluationManager(config=eval_cfg, - scenario_name=scenario_type, - agent_name=agent_name) + # Check if using SUMO-only mode + simulator = self.config.get('meta', {}).get('simulator', 'carla') + + if simulator == 'sumo': + # SUMO-only mode (no CARLA, no CAV world needed) + logger.info("Initializing SUMO-only MARL environment") + + # Create SUMO MARL environment directly + self.marl_env = SumoMARLEnv(config=self.config) + + # Set placeholders for CARLA components (not used in SUMO mode) + self.cav_world = None + self.scenario_manager = None + self.carla_client = None + self.carla_world = None + self.carla_spectator = None + self.carla_monitor = None + + # Populate states from config for SUMO mode + simulation_cfg = self.config.get('scenario', {}).get('simulation', {}) + self.states = { + 'max_steps': simulation_cfg.get('max_steps', 2400), + 'max_episodes': simulation_cfg.get('max_episodes', 1000) + } + + # Override max_episodes for evaluation mode (training_mode: false) + marl_cfg = self.config.get('MARL', {}) + training_mode = marl_cfg.get('training', {}).get('training_mode', True) + if not training_mode: + self.states['max_episodes'] = 1 + logger.info("Evaluation mode: setting max episode to 1 (training_mode: false)") + + # Create Evaluation manager + scenario_type = self.config.get("meta", {}).get("scenario_type", None) + agent_name = self.config.get("agents", {}).get("agent_type", None) + eval_cfg = self.config.get('evaluation', {}) + self.evaluation_manager = EvaluationManager(config=eval_cfg, + scenario_name=scenario_type, + agent_name=agent_name) + else: + # CARLA mode (standard) + logger.info("Initializing CARLA MARL environment") + + # 1. Create CAV world + self.cav_world = CavWorld(apply_ml=self.config.opt.apply_ml) + + # 2. Create Scenario Manager + self.scenario_manager = ScenarioBuilder.build_from_config( + config=self.config, + cav_world=self.cav_world, + ) + self.states = self.scenario_manager.states + self.carla_client = self.scenario_manager.client + self.carla_world = self.scenario_manager.world + + # 3. Create Spectator for GUI + spectator_config = self.config.get('spectator', {}) + self.carla_spectator = CarlaSpectator( + self.carla_world, spectator_config) + + # 4. Create MARL environment + Marl_cfg = self.config.get('MARL', {}) + self.marl_env = MARLEnv(self.scenario_manager, + config=Marl_cfg) + + # 5. Create CARLA monitor + marl_tm = self.scenario_manager.traffic_manager + self.carla_monitor = CarlaMonitor(self.carla_world, + marl_tm=marl_tm) + + # 6. Create Evaluation manager + scenario_type = self.config.get("meta", {}).get("scenario_type", None) + agent_name = self.config.get("agents", {}).get("agent_type", None) + eval_cfg = self.config.get('evaluation', {}) + self.evaluation_manager = EvaluationManager(config=eval_cfg, + scenario_name=scenario_type, + agent_name=agent_name) + + # 7. Initialize world reset manager if configured + world_reset_cfg = self.config.get('world_reset', {}) + if (world_reset_cfg.get('reset_frequency', 0) > 0 or + world_reset_cfg.get('auto_reset', {}).get('enabled', False)): + from opencda_marl.core.world_reset_manager import WorldResetManager + self.world_reset_manager = WorldResetManager(world_reset_cfg, self) + logger.info("WorldResetManager initialized for CARLA memory management") # --------------------------------------------------------------------- # # main thread # --------------------------------------------------------------------- # def get_metrics(self): - metrics = self.states.copy() - if self.marl_env: - metrics.update(self.marl_env.get_episode_metrics()) - return metrics + simulator = self.config.get('meta', {}).get('simulator', 'carla') + + if simulator == 'sumo': + # SUMO mode: get metrics directly from environment + metrics = {} + if self.marl_env: + metrics.update(self.marl_env.get_episode_metrics()) + return metrics + else: + # CARLA mode: combine scenario states and environment metrics + metrics = self.states.copy() + if self.marl_env: + metrics.update(self.marl_env.get_episode_metrics()) + # Add pending spawns from traffic manager for accurate throughput calculation + if self.scenario_manager: + traffic_info = self.scenario_manager.get_traffic_info() + metrics['pending_spawns'] = traffic_info.get('pending_spawns', 0) + return metrics def step(self): + import time + step_start = time.perf_counter() + # Call pre-step callbacks for callback in self.pre_step_callbacks: callback() @@ -121,7 +190,10 @@ def step(self): rewards = self.marl_env.get_current_step_rewards() self.evaluation_manager.update_step(metrics, rewards) - #print(f"states: {self.states}, rewards: {rewards}, metrics: {metrics}") + # Record step time for world reset manager performance monitoring + step_duration = time.perf_counter() - step_start + if self.world_reset_manager: + self.world_reset_manager.record_step_time(step_duration) def run(self): """ @@ -150,8 +222,9 @@ def reset_episode(self): episode_metrics = self.marl_env.reset_episode() logger.info(f"Episode metrics: {episode_metrics}") - # Reset scenario manager - self.scenario_manager.reset_episode() + # Reset scenario manager (only in CARLA mode) + if self.scenario_manager is not None: + self.scenario_manager.reset_episode() # Call episode callbacks for callback in self.episode_callbacks: @@ -165,6 +238,11 @@ def reset_episode(self): import gc gc.collect() + # Check for world reset AFTER episode completes (safe timing) + # This prevents CARLA server-side memory accumulation slowdown + if self.world_reset_manager: + self.world_reset_manager.on_episode_end() + def run_gui_mode(self): """ Run in GUI mode with step-by-step control. diff --git a/opencda_marl/core/__init__.py b/opencda_marl/core/__init__.py index 7d80c85..6e4a443 100644 --- a/opencda_marl/core/__init__.py +++ b/opencda_marl/core/__init__.py @@ -2,9 +2,10 @@ Author : AXIBA leolihao@arizona.edu Date : 2025-08-29 20:41:21 FilePath : /OpenCDA-MARL/opencda_marl/core/__init__.py -Description : +Description : Copyright (c) 2025 by AXIBA (leolihao@arizona.edu), All Rights Reserved. ''' from .agent_manager import MARLAgentManager +from .world_reset_manager import WorldResetManager -__all__ = ["MARLAgentManager"] \ No newline at end of file +__all__ = ["MARLAgentManager", "WorldResetManager"] \ No newline at end of file diff --git a/opencda_marl/core/adapter/vehicle_adapter.py b/opencda_marl/core/adapter/vehicle_adapter.py index 1ef10ea..bbdd50d 100644 --- a/opencda_marl/core/adapter/vehicle_adapter.py +++ b/opencda_marl/core/adapter/vehicle_adapter.py @@ -15,10 +15,17 @@ from opencda_marl.core.adapter.vehicle_defaults import get_vehicle_manager_defaults from opencda_marl.core.adapter.exception import CollisionException -from opencda_marl.core.safety.marl_safety_manager import MARLSafetyManager +from opencda_marl.core.safety.marl_collision_sensor import MARLCollisionSensor from opencda_marl.core.agents import AgentFactory from opencda_marl.core.agents.vanilla_agent import VanillaAgent +# Default nearby vehicle detection constants (can be overridden by config) +DEFAULT_NEARBY_DETECTION_RADIUS = 50.0 # meters - detection radius for nearby vehicles +DEFAULT_MAX_NEARBY_VEHICLES = 5 # Maximum number of nearby vehicles to track (K slots) +DEFAULT_FEATURES_PER_VEHICLE = 7 # Features per vehicle: rel_x, rel_y, rel_vx, rel_vy, heading_diff, distance, ttc +DEFAULT_MAX_RELATIVE_VELOCITY = 40.0 # m/s (~144 km/h) - for normalization +DEFAULT_MAX_TTC = 10.0 # seconds - TTC values clamped to this maximum + class MARLVehicleAdapter: @@ -38,13 +45,32 @@ def __init__(self, config: Dict[str, Any], self.world = vehicle.get_world() self.dump_data = dump_data self.agent_type = agent_type - + # Store target speed for monitoring self.target_speed = 0.0 - + + # Load nearby vehicle config from MARL config (or use defaults) + self._init_nearby_vehicle_config(config) + self.vm = self.get_vm() + + # Replace SafetyManager with MARLSafetyManager for proper collision detection if hasattr(self.vm, 'safety_manager'): self._use_marl_safety_manager() + + def _init_nearby_vehicle_config(self, config: Dict[str, Any]): + """Initialize nearby vehicle detection parameters from config.""" + # Try to get config from MARL.td3.nearby_vehicle_config + marl_config = config.get('MARL', {}) + td3_config = marl_config.get('td3', {}) + nearby_config = td3_config.get('nearby_vehicle_config', {}) + + # Load values from config with defaults + self.nearby_detection_radius = nearby_config.get('detection_radius', DEFAULT_NEARBY_DETECTION_RADIUS) + self.max_nearby_vehicles = nearby_config.get('max_vehicles', DEFAULT_MAX_NEARBY_VEHICLES) + self.features_per_vehicle = nearby_config.get('features_per_vehicle', DEFAULT_FEATURES_PER_VEHICLE) + self.max_relative_velocity = nearby_config.get('max_relative_velocity', DEFAULT_MAX_RELATIVE_VELOCITY) + self.max_ttc = nearby_config.get('max_ttc', DEFAULT_MAX_TTC) # --------------------------------------------------------------------- # # Public control API # --------------------------------------------------------------------- # @@ -53,7 +79,16 @@ def step(self, target_speed: float = None): # Store target speed for monitoring if target_speed is not None: self.target_speed = target_speed - + else: + # During warmup (target_speed=None): use current vehicle speed as "target" + # This reflects what vanilla agent is commanding, for TensorBoard tracking + try: + velocity = self.vm.vehicle.get_velocity() + current_speed = 3.6 * (velocity.x**2 + velocity.y**2 + velocity.z**2)**0.5 + self.target_speed = current_speed + except Exception: + pass # Keep previous value if velocity unavailable + self.vm.update_info() if self.check_collision(): @@ -76,14 +111,17 @@ def set_target_speed(self, target_speed: float): def check_collision(self) -> bool: """ - Simple collision check using MARL safety manager. + Check collision using MARLSafetyManager's check_collision method. + + Note: MARLSafetyManager uses MARLCollisionSensor which has a non-resetting + return_status(). This allows update_info() to report collision status without + consuming it, and check_collision() can then check and reset the flag. """ try: if self.vm.safety_manager: return self.vm.safety_manager.check_collision() except Exception as e: - logger.error( - f"Warning: Could not check collision for vehicle {self.actor_id}: {e}") + logger.debug(f"Could not check collision for vehicle {self.actor_id}: {e}") return False def get_observation(self) -> Dict[str, Any]: @@ -104,7 +142,7 @@ def get_observation(self) -> Dict[str, Any]: if hasattr(self.vm, 'agent'): agent = self.vm.agent - # Get speed using getter method + # Get speed (already in km/h from agent) if hasattr(agent, 'get_speed'): obs['speed'] = round(agent.get_speed(), 2) elif hasattr(agent, '_ego_speed'): @@ -156,6 +194,12 @@ def get_observation(self) -> Dict[str, Any]: obs['distance_to_front_vehicle'] = self._calculate_distance_to_front_vehicle() obs['lane_position'] = self._classify_lane_position() + # Add nearby vehicle features for Phase 3 enhancement (35D) + nearby_features, min_ttc = self._compute_nearby_vehicle_features() + obs['nearby_vehicles'] = nearby_features # 35D list (5 vehicles × 7 features) + obs['min_ttc'] = min_ttc # For TTC-based reward calculation + obs['distance_to_destination'] = self._calculate_distance_to_destination() # For progress reward + # Add new enhanced DQN features (9D feature set) # 1. Relative position to intersection (replaces absolute position) rel_x, rel_y = self._calculate_relative_position_to_intersection() @@ -463,51 +507,220 @@ def _get_vehicle_heading_angle(self) -> float: # CARLA uses degrees, convert to radians and normalize to [-π, π] yaw_degrees = vehicle_transform.rotation.yaw yaw_radians = np.radians(yaw_degrees) - + # Normalize to [-π, π] while yaw_radians > np.pi: yaw_radians -= 2 * np.pi while yaw_radians < -np.pi: yaw_radians += 2 * np.pi - + return yaw_radians - + except Exception as e: logger.debug(f"Error getting vehicle heading angle: {e}") return 0.0 + # --------------------------------------------------------------------- # + # Nearby vehicle detection for MARL + # --------------------------------------------------------------------- # + def _get_nearby_vehicles(self) -> list: + """ + Get nearby vehicles within detection radius, sorted by distance. + Uses bounding box pre-filter for efficiency. + Returns: + List of tuples: [(vehicle, distance), ...] sorted by distance, max K vehicles + """ + try: + current_location = self.vehicle.get_location() + all_vehicles = self.world.get_actors().filter('vehicle.*') - # --------------------------------------------------------------------- # - # Private functions - # --------------------------------------------------------------------- # + nearby = [] + for vehicle in all_vehicles: + if vehicle.id == self.vehicle.id: + continue # Skip self - def _use_marl_safety_manager(self): + if not vehicle.is_alive: + continue + + other_loc = vehicle.get_location() + + # Quick bounding box check (faster than full distance calculation) + if (abs(other_loc.x - current_location.x) > self.nearby_detection_radius or + abs(other_loc.y - current_location.y) > self.nearby_detection_radius): + continue + + distance = current_location.distance(other_loc) + if distance <= self.nearby_detection_radius: + nearby.append((vehicle, distance)) + + # Sort by distance and return top K + nearby.sort(key=lambda x: x[1]) + return nearby[:self.max_nearby_vehicles] + + except Exception as e: + logger.debug(f"Error getting nearby vehicles for {self.actor_id}: {e}") + return [] + + def _calculate_ttc_to_vehicle(self, other_vehicle: carla.Actor) -> float: """ - Minimal replacement - just swap the safety manager with MARL version. + Calculate Time-to-Collision (TTC) to another vehicle using constant velocity assumption. + + Uses quadratic equation: |rel_pos + t*rel_vel|² = collision_radius² + + Args: + other_vehicle: The other CARLA vehicle actor + + Returns: + float: TTC in seconds (inf if no collision predicted within horizon) """ try: - params = self.vm_cfg.get('safety_manager', { - 'collision_sensor': {'history_size': 4000, 'col_thresh': 50}, - 'stuck_dector': {'len_thresh': 300, 'speed_thresh': 0.05}, - 'offroad_dector': {'speed_thresh': 5}, - 'traffic_light_detector': {'speed_thresh': 20}, - 'print_message': False - }) + ego_loc = self.vehicle.get_location() + ego_vel = self.vehicle.get_velocity() + other_loc = other_vehicle.get_location() + other_vel = other_vehicle.get_velocity() + + # Relative position and velocity + rel_x = other_loc.x - ego_loc.x + rel_y = other_loc.y - ego_loc.y + rel_vx = other_vel.x - ego_vel.x + rel_vy = other_vel.y - ego_vel.y + + collision_radius = 4.0 # Combined vehicle radii (meters) + + # Quadratic equation coefficients: at² + bt + c = 0 + a = rel_vx**2 + rel_vy**2 + b = 2 * (rel_x * rel_vx + rel_y * rel_vy) + c = rel_x**2 + rel_y**2 - collision_radius**2 + + # No relative motion - check if already colliding + if abs(a) < 1e-6: + return float('inf') + + discriminant = b**2 - 4 * a * c + + # No collision trajectory + if discriminant < 0: + return float('inf') + + # Find smallest positive root + sqrt_disc = np.sqrt(discriminant) + t1 = (-b - sqrt_disc) / (2 * a) + t2 = (-b + sqrt_disc) / (2 * a) + + if t1 > 0: + return t1 + elif t2 > 0: + return t2 + else: + return float('inf') # Collision in past - if self.vm.safety_manager: - self.vm.safety_manager.destroy() + except Exception as e: + logger.debug(f"Error calculating TTC: {e}") + return float('inf') - self.vm.safety_manager = MARLSafetyManager( - self.cav_world, - self.vehicle, - params - ) + def _compute_nearby_vehicle_features(self) -> tuple: + """ + Compute normalized features for nearby vehicles (35D total = 5 slots × 7 features). + + Per-vehicle features (7D): + - rel_x: Relative X position (normalized to [-1, 1]) + - rel_y: Relative Y position (normalized to [-1, 1]) + - rel_vx: Relative velocity X (normalized to [-1, 1]) + - rel_vy: Relative velocity Y (normalized to [-1, 1]) + - heading_diff: Heading difference (normalized to [-1, 1]) + - distance: Euclidean distance (normalized to [0, 1]) + - ttc: Time-to-collision (normalized to [0, 1]) + + Empty slots are filled with safe defaults (distance=1.0, ttc=1.0). + + Returns: + Tuple[List[float], float]: (35D feature vector, minimum TTC across all vehicles) + """ + try: + nearby_vehicles = self._get_nearby_vehicles() + + ego_loc = self.vehicle.get_location() + ego_vel = self.vehicle.get_velocity() + ego_heading = np.radians(self.vehicle.get_transform().rotation.yaw) + + features = [] + min_ttc = float('inf') + + for i in range(self.max_nearby_vehicles): + if i < len(nearby_vehicles): + vehicle, distance = nearby_vehicles[i] + other_loc = vehicle.get_location() + other_vel = vehicle.get_velocity() + other_heading = np.radians(vehicle.get_transform().rotation.yaw) + + # Relative position (normalized by detection radius) + rel_x = (other_loc.x - ego_loc.x) / self.nearby_detection_radius + rel_y = (other_loc.y - ego_loc.y) / self.nearby_detection_radius + + # Relative velocity (normalized by max velocity) + rel_vx = (other_vel.x - ego_vel.x) / self.max_relative_velocity + rel_vy = (other_vel.y - ego_vel.y) / self.max_relative_velocity + + # Heading difference (normalized by π) + heading_diff = other_heading - ego_heading + # Wrap to [-π, π] + while heading_diff > np.pi: + heading_diff -= 2 * np.pi + while heading_diff < -np.pi: + heading_diff += 2 * np.pi + heading_diff_norm = heading_diff / np.pi + + # Distance (normalized by detection radius) + distance_norm = distance / self.nearby_detection_radius + + # TTC (normalized by max TTC, clamped) + ttc = self._calculate_ttc_to_vehicle(vehicle) + min_ttc = min(min_ttc, ttc) + ttc_norm = min(ttc, self.max_ttc) / self.max_ttc + + features.extend([ + np.clip(rel_x, -1.0, 1.0), + np.clip(rel_y, -1.0, 1.0), + np.clip(rel_vx, -1.0, 1.0), + np.clip(rel_vy, -1.0, 1.0), + np.clip(heading_diff_norm, -1.0, 1.0), + np.clip(distance_norm, 0.0, 1.0), + np.clip(ttc_norm, 0.0, 1.0) + ]) + else: + # Empty slot: zeros except distance and TTC = 1.0 (safe/far away) + features.extend([0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0]) + + return features, min_ttc except Exception as e: - logger.error( - f"Warning: Failed to initialize MARL safety manager for vehicle {self.actor_id}: {e}") + logger.debug(f"Error computing nearby vehicle features: {e}") + # Return safe defaults: K slots × 7 features with safe values + default_features = [] + for _ in range(self.max_nearby_vehicles): + default_features.extend([0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0]) + return default_features, float('inf') + + def _calculate_distance_to_destination(self) -> float: + """ + Calculate distance to destination for progress reward calculation. + + Returns: + float: Distance to destination in meters (999.0 if not available) + """ + try: + if hasattr(self.vm, 'agent') and hasattr(self.vm.agent, '_local_planner'): + planner = self.vm.agent._local_planner + if hasattr(planner, '_destination') and planner._destination: + current_loc = self.vehicle.get_location() + dest_loc = planner._destination + return current_loc.distance(dest_loc) + return 999.0 + except Exception as e: + logger.debug(f"Error calculating distance to destination: {e}") + return 999.0 # --------------------------------------------------------------------- # # Helper functions @@ -548,7 +761,7 @@ def get_vm(self): agent = AgentFactory.get_agent(self.agent_type, self.vehicle, self.carla_map, self.config) - return VehicleManager( + vm = VehicleManager( vehicle=self.vehicle, config_yaml=self.vm_cfg, application=['single'], @@ -556,6 +769,8 @@ def get_vm(self): cav_world=self.cav_world, agent=agent ) + + return vm except Exception as e: logger.error( f"Error getting agent for vehicle {self.actor_id}: {e}") @@ -563,6 +778,50 @@ def get_vm(self): traceback.print_exc() return None + def _use_marl_safety_manager(self): + """ + Replace collision sensor with MARLCollisionSensor in existing SafetyManager. + + Instead of recreating the entire SafetyManager (which would create duplicate + IMUSensors), we just replace the collision sensor in-place. This avoids + the "streaming client: connection failed" errors from orphaned sensors. + + MARLCollisionSensor has a non-resetting return_status() which allows proper + collision detection in the MARL training loop. + """ + try: + safety_manager = self.vm.safety_manager + if not safety_manager: + return + + # Get collision sensor params + safety_params = self.vm_cfg.get('safety_manager', {}) + collision_params = safety_params.get('collision_sensor', {}) + + # Destroy only the collision sensor (sensors[0]) + if len(safety_manager.sensors) > 0: + safety_manager.sensors[0].destroy() + + # Replace with MARLCollisionSensor + safety_manager.sensors[0] = MARLCollisionSensor( + self.vehicle, collision_params) + + # Add check_collision method to the existing safety manager + def check_collision(): + if len(safety_manager.sensors) > 0: + sensor = safety_manager.sensors[0] + if isinstance(sensor, MARLCollisionSensor): + return sensor.check_and_reset() + return False + + safety_manager.check_collision = check_collision + + logger.debug(f"Vehicle {self.actor_id}: Replaced CollisionSensor with MARLCollisionSensor") + except Exception as e: + logger.error(f"Error replacing collision sensor for vehicle {self.actor_id}: {e}") + import traceback + traceback.print_exc() + # --------------------------------------------------------------------- # # Public API # --------------------------------------------------------------------- # @@ -579,12 +838,17 @@ def get_carla_id(self) -> int: # --------------------------------------------------------------------- # def destroy(self): - """Destroy the vehicle manager and all its sensors.""" + """ + Destroy the vehicle manager and all its sensors. + + Note: The VehicleManager.destroy() now handles proper sensor cleanup + before destroying the vehicle to prevent Signal 11 crashes. + """ try: self.cav_world.remove_vehicle_manager(self.vm) if self.vm: self.vm.destroy() self.vm = None except Exception as e: - logger.error(f"Error destroying vehicle manager: {e}") + logger.debug(f"Error destroying vehicle manager: {e}") diff --git a/opencda_marl/core/agent_manager.py b/opencda_marl/core/agent_manager.py index 90c042a..503b96f 100644 --- a/opencda_marl/core/agent_manager.py +++ b/opencda_marl/core/agent_manager.py @@ -55,8 +55,13 @@ def _step_all_vms(self, target_speed: Dict[int, float] = {}): for i, adapter in enumerate(self._vehicle_adapters): try: - if adapter.actor_id in target_speed.keys(): - agent_target_speed = target_speed[adapter.actor_id] + # Handle both int and string keys from MARL manager + # CARLA uses int actor IDs, but observation extractor may use string keys + actor_id = adapter.actor_id + if actor_id in target_speed: + agent_target_speed = target_speed[actor_id] + elif str(actor_id) in target_speed: + agent_target_speed = target_speed[str(actor_id)] else: agent_target_speed = None adapter.step(agent_target_speed) @@ -86,18 +91,20 @@ def _step_all_vms(self, target_speed: Dict[int, float] = {}): traceback.print_exc() raise e + # Remove completed/collided vehicles in reverse order for i in reversed(remove_indices): self._remove_adapter_by_index(i) def _remove_adapter_by_index(self, index: int): + """Remove a single vehicle adapter.""" if 0 <= index < len(self._vehicle_adapters): adapter = self._vehicle_adapters[index] vehicle = self._spawned_vehicles[index] - # Clean up the adapter (this will destroy the VehicleManager) + # Destroy adapter (handles VehicleManager cleanup) adapter.destroy() - # Destroy the CARLA vehicle actor + # Destroy vehicle if still alive if vehicle.is_alive: vehicle.destroy() @@ -234,12 +241,13 @@ def reset(self): def cleanup(self): """Clean up all spawned vehicles and their adapters.""" logger.info( - f"Cleaning up {len(self._vehicle_adapters)} vehicle adapters" + f"Cleaning up {len(self._vehicle_adapters)} vehicle adapters " f"and {len(self._spawned_vehicles)} vehicles") - for _, adapter in enumerate(self._vehicle_adapters): + + for adapter in self._vehicle_adapters: adapter.destroy() - for _, vehicle in enumerate(self._spawned_vehicles): + for vehicle in self._spawned_vehicles: if hasattr(vehicle, 'is_alive') and vehicle.is_alive: vehicle.destroy() @@ -247,4 +255,11 @@ def cleanup(self): self._vehicle_adapters.clear() self._spawned_vehicles.clear() + # IMPORTANT: Tick world to process destroy commands and release GPU memory + # Without this, CARLA keeps resources allocated on the GPU + try: + self.world.tick() + except Exception as e: + logger.debug(f"World tick after cleanup failed: {e}") + logger.info("MARLAgentManager cleanup completed") diff --git a/opencda_marl/core/agents/marl_agent.py b/opencda_marl/core/agents/marl_agent.py index a25cdbd..b6b565e 100644 --- a/opencda_marl/core/agents/marl_agent.py +++ b/opencda_marl/core/agents/marl_agent.py @@ -13,6 +13,11 @@ class MARLAgent(VanillaAgent): """ MARL agent implementing speed-only control with multiple RL algorithms. + + Key difference from VanillaAgent: + - When RL provides target_speed: Uses RL speed directly (bypasses BasicAgent's autonomous control) + - When target_speed is None (warmup): Falls back to VanillaAgent's default behavior + - Path/steering still controlled by local planner """ def __init__(self, vehicle, carla_map, config_yaml): @@ -34,4 +39,54 @@ def __init__(self, vehicle, carla_map, config_yaml): # Main Step Function # --------------------------------------------------------------------- # def run_step(self, target_speed=None): - return super().run_step(target_speed) + """ + Execute one step with RL-controlled speed. + + CRITICAL: This method overrides VanillaAgent to ensure RL target_speed + is actually applied to the vehicle, not ignored by BasicAgent. + + When target_speed is provided by RL: + - Use RL target_speed directly (RL controls speed) + - Call local_planner.run_step() to advance waypoints along route + - Return updated target_location for path following + + When target_speed is None (warmup or baseline): + - Fall back to VanillaAgent's default behavior + + Args: + target_speed: Speed in km/h from RL algorithm, or None for vanilla behavior + + Returns: + Tuple[float, carla.Location]: (clamped_speed, target_location) + """ + # If no RL target speed provided, use vanilla agent behavior + if target_speed is None: + return super().run_step(target_speed) + + # Check if agent is properly initialized + if not self._ego_pos: + logger.warning("MARLAgent: ego position not set, returning zero speed") + return 0.0, None + + # Check destination reached + if self.is_close_to_destination(): + raise StopIteration("Destination reached - simulation complete") + + # RL mode: Use RL-provided target speed, but still update local planner + local_planner = self.get_local_planner() + + # CRITICAL: Call local planner's run_step to advance waypoints along the route + # This ensures the waypoint buffer is updated and vehicle follows the planned path + # Without this call, the vehicle would keep targeting the same stale waypoint + if local_planner: + # Call run_step to update waypoint buffers (no parameters needed for this LocalPlanner) + local_planner.run_step() + # Get target location from the updated target_waypoint + target_location = local_planner.target_waypoint.transform.location if local_planner.target_waypoint else None + else: + target_location = self._ego_pos.location if self._ego_pos else None + + # Clamp target_speed to safe bounds [0, max_speed] + clamped_speed = max(0.0, min(self.max_speed, target_speed)) + + return clamped_speed, target_location diff --git a/opencda_marl/core/agents/vanilla_agent.py b/opencda_marl/core/agents/vanilla_agent.py index cce61eb..c279405 100644 --- a/opencda_marl/core/agents/vanilla_agent.py +++ b/opencda_marl/core/agents/vanilla_agent.py @@ -171,6 +171,9 @@ def run_step(self, target_speed=None): """ Execute one step of navigation. Returns OpenCDA-compatible format: (target_speed, target_location) + + IMPORTANT: If target_speed is provided (from RL algorithm), it is used directly + for the controller. This ensures RL actually controls vehicle speed. """ # Check if agent is properly initialized if not self._ego_pos: @@ -182,6 +185,31 @@ def run_step(self, target_speed=None): # Signal completion without forceful exit - let the scenario manager handle cleanup raise StopIteration("Destination reached - simulation complete") + # Get local planner + local_planner = self.get_local_planner() + + # CRITICAL FIX: If RL provides target_speed, still call local_planner.run_step() + # to advance waypoints along the route. Otherwise vehicle won't follow the route. + if target_speed is not None: + if local_planner: + # Call run_step to advance waypoints (no parameters needed for this LocalPlanner) + local_planner.run_step() + # Get target location from updated waypoint + target_location = local_planner.target_waypoint.transform.location if local_planner.target_waypoint else None + else: + target_location = self._ego_pos.location if self._ego_pos else None + + # Clamp to safe range [0, max_speed] + clamped_speed = max(0.0, min(target_speed, self.max_speed)) + return clamped_speed, target_location + + # Fallback behavior: only used when no RL target_speed provided (warmup phase) + # Get target location from local planner for fallback path + if local_planner and hasattr(local_planner, 'target_waypoint') and local_planner.target_waypoint: + target_location = local_planner.target_waypoint.transform.location + else: + target_location = self._ego_pos.location if self._ego_pos else None + try: # Get control from BasicAgent's CARLA implementation control = super().run_step() @@ -189,12 +217,10 @@ def run_step(self, target_speed=None): # Extract target speed from control (convert from throttle/brake to speed) if control.throttle > 0: # Use configured max_speed when accelerating - calculated_target_speed = min( - self.max_speed, target_speed if target_speed else self.max_speed) + calculated_target_speed = self.max_speed elif control.brake > 0: # Reduce speed when braking - calculated_target_speed = max( - 0, self._ego_speed - 10) # Gradual brake + calculated_target_speed = max(0, self._ego_speed - 10) # Gradual brake else: # Maintain current speed calculated_target_speed = self._ego_speed @@ -202,16 +228,7 @@ def run_step(self, target_speed=None): except Exception as e: print(f"VanillaAgent: Error in BasicAgent.run_step(): {e}") # Fallback to conservative behavior - calculated_target_speed = min( - 5.0, self._ego_speed) # Slow down safely - - # Get target location from local planner - local_planner = self.get_local_planner() - if local_planner and hasattr(local_planner, 'target_waypoint') and local_planner.target_waypoint: - target_location = local_planner.target_waypoint.transform.location - else: - # Fallback: use current position if no waypoint available - target_location = self._ego_pos.location if self._ego_pos else None + calculated_target_speed = min(5.0, self._ego_speed) # Slow down safely return calculated_target_speed, target_location @@ -219,7 +236,7 @@ def run_step(self, target_speed=None): # Public getter methods for state information # --------------------------------------------------------------------- # def get_speed(self) -> float: - """Get current ego vehicle speed in m/s.""" + """Get current ego vehicle speed in km/h.""" return self._ego_speed def get_position(self) -> tuple: diff --git a/opencda_marl/core/marl/algorithms/__init__.py b/opencda_marl/core/marl/algorithms/__init__.py index bf4e792..7632b52 100644 --- a/opencda_marl/core/marl/algorithms/__init__.py +++ b/opencda_marl/core/marl/algorithms/__init__.py @@ -7,10 +7,19 @@ This package contains implementations of various reinforcement learning algorithms for the MARL agent system. +Available algorithms: +- QLearningAlgorithm: Tabular Q-learning for discrete state/action spaces +- DQNAlgorithm: Deep Q-Network for continuous state, discrete action spaces +- TD3Algorithm: Twin Delayed DDPG for continuous state/action spaces (off-policy) +- MAPPOAlgorithm: Multi-Agent PPO for cooperative multi-agent learning (on-policy) +- SACAlgorithm: Soft Actor-Critic with auto-tuning entropy (off-policy) + Copyright (c) 2025 by AXIBA (leolihao@arizona.edu), All Rights Reserved. ''' from .q_learning import QLearningAlgorithm from .dqn import DQNAlgorithm from .td3 import TD3Algorithm +from .mappo import MAPPOAlgorithm +from .sac import SACAlgorithm -__all__ = ['QLearningAlgorithm', 'DQNAlgorithm', 'TD3Algorithm'] \ No newline at end of file +__all__ = ['QLearningAlgorithm', 'DQNAlgorithm', 'TD3Algorithm', 'MAPPOAlgorithm', 'SACAlgorithm'] \ No newline at end of file diff --git a/opencda_marl/core/marl/algorithms/base_algorithm.py b/opencda_marl/core/marl/algorithms/base_algorithm.py index f0e7762..28c12a4 100644 --- a/opencda_marl/core/marl/algorithms/base_algorithm.py +++ b/opencda_marl/core/marl/algorithms/base_algorithm.py @@ -6,10 +6,21 @@ Copyright (c) 2025 by AXIBA (leolihao@arizona.edu), All Rights Reserved. ''' from abc import ABC, abstractmethod -from typing import Any, Dict +from typing import Any, Dict, Optional +from collections import deque +import os +from datetime import datetime import numpy as np from loguru import logger +# TensorBoard for training visualization +try: + from torch.utils.tensorboard import SummaryWriter + TENSORBOARD_AVAILABLE = True +except ImportError: + TENSORBOARD_AVAILABLE = False + logger.warning("TensorBoard not available. Install with: pip install tensorboard") + class BaseAlgorithm(ABC): """ @@ -40,9 +51,463 @@ def __init__(self, config: Dict[str, Any], state_dim: int, action_dim: int): self.training_step = 0 self.episode_count = 0 + # Reward tracking for convergence analysis + self.reward_window_size = config.get('reward_window_size', 10) + self.reward_history: deque = deque(maxlen=100) # Full history (last 100 episodes) + self.episode_length_history: deque = deque(maxlen=100) + + # Success/collision tracking for MARL traffic convergence + self.success_rate_history: deque = deque(maxlen=100) + self.collision_rate_history: deque = deque(maxlen=100) + + # Convergence detection parameters (more lenient for MARL traffic scenarios) + self.convergence_threshold = config.get('convergence_threshold', 0.15) # 15% CV threshold (was 5%) + self.convergence_window = config.get('convergence_window', 10) # Episodes to check + self.min_episodes_for_convergence = config.get('min_episodes_for_convergence', 20) # Need at least 20 episodes + self.is_converged = False + self.convergence_episode = None # Episode when convergence was detected + + # Initialize TensorBoard logging + self._init_tensorboard(config) + logger.success( f"Initialized {self.__class__.__name__} with state_dim={state_dim}, action_dim={action_dim}") + def _init_tensorboard(self, config: Dict[str, Any]): + """Initialize TensorBoard logging based on configuration.""" + # Get tensorboard config (can be nested under algorithm or at root level) + tb_config = config.get('tensorboard', {}) + if isinstance(tb_config, bool): + # Handle simple boolean config + tb_config = {'enabled': tb_config} + + self.tb_enabled = tb_config.get('enabled', True) and TENSORBOARD_AVAILABLE + self.writer: Optional[SummaryWriter] = None + + if self.tb_enabled: + # Configurable log directory + base_dir = tb_config.get('log_dir', 'runs') + algo_name = self.__class__.__name__.lower().replace('algorithm', '') + timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') + + # Allow custom run name + run_name = tb_config.get('run_name', timestamp) + self.tensorboard_dir = os.path.join(base_dir, algo_name, run_name) + + self.writer = SummaryWriter(log_dir=self.tensorboard_dir) + logger.info(f"TensorBoard logging enabled: {self.tensorboard_dir}") + logger.info(f"View with: tensorboard --logdir={base_dir}") + + # Configurable metrics to log + self.tb_log_frequency = tb_config.get('log_frequency', 1) # Log every N steps + self.tb_metrics = tb_config.get('metrics', { + 'losses': True, + 'q_values': True, + 'buffer': True, + 'episode': True, + 'rewards': True + }) + + def log_scalar(self, tag: str, value: float, step: int = None, category: str = None): + """ + Log a scalar value to TensorBoard. + + Args: + tag: Metric name + value: Scalar value + step: Global step (defaults to training_step) + category: Optional category to check if logging is enabled + """ + if self.writer is None: + return + + # Check if this category of metrics should be logged + if category and not self.tb_metrics.get(category, True): + return + + # Use training_step if step not provided + if step is None: + step = self.training_step + + # Respect log frequency + if step % self.tb_log_frequency != 0: + return + + self.writer.add_scalar(tag, value, step) + + def log_scalars(self, main_tag: str, tag_scalar_dict: Dict[str, float], + step: int = None, category: str = None): + """ + Log multiple scalars under a main tag. + + Args: + main_tag: Main category tag + tag_scalar_dict: Dictionary of {tag: value} + step: Global step + category: Optional category to check if logging is enabled + """ + if self.writer is None: + return + + if category and not self.tb_metrics.get(category, True): + return + + if step is None: + step = self.training_step + + if step % self.tb_log_frequency != 0: + return + + self.writer.add_scalars(main_tag, tag_scalar_dict, step) + + def log_histogram(self, tag: str, values: np.ndarray, step: int = None): + """Log a histogram of values.""" + if self.writer is None: + return + + if step is None: + step = self.training_step + + self.writer.add_histogram(tag, values, step) + + def log_episode_metrics(self, episode_reward: float, episode_length: int, + success_rate: float = 0.0, collision_rate: float = 0.0, + near_miss_count: int = 0, + ttc_violation_rate: float = 0.0, + additional_metrics: Dict[str, float] = None, + traffic_metrics: Dict[str, float] = None): + """ + Log episode-level metrics to TensorBoard with learning quality analysis. + + Args: + episode_reward: Total episode reward + episode_length: Number of steps in episode + success_rate: Success rate (0-1) + collision_rate: Collision rate (0-1) + near_miss_count: Number of near-miss events (TTC < threshold without collision) + ttc_violation_rate: Percentage of TTC checks that were below safe threshold + additional_metrics: Additional custom metrics to log + traffic_metrics: Traffic performance metrics (avg_speed, speed_variance, etc.) + """ + # Track reward and episode length history (always track, even without TensorBoard) + self.reward_history.append(episode_reward) + self.episode_length_history.append(episode_length) + + # Track success/collision rates for MARL traffic convergence analysis + self.success_rate_history.append(success_rate) + self.collision_rate_history.append(collision_rate) + + # Compute learning quality metrics + reward_ma, reward_var, reward_std = self._compute_reward_statistics() + #length_ma = self._compute_episode_length_ma() + + # Check for convergence (MARL-aware) + self._check_convergence() + + if self.writer is None or not self.tb_metrics.get('episode', True): + return + + # Core episode metrics + self.writer.add_scalar('Episode/reward', episode_reward, self.episode_count) + self.writer.add_scalar('Episode/success_rate', success_rate, self.episode_count) + self.writer.add_scalar('Episode/collision_rate', collision_rate, self.episode_count) + self.writer.add_scalar('Episode/length', episode_length, self.episode_count) + + # Safety metrics: Near-miss count and TTC violation rate + # Decreasing near-misses = agent learning to avoid dangerous situations + self.writer.add_scalar('Safety/near_miss_count', near_miss_count, self.episode_count) + # TTC violation rate: % of TTC checks with TTC < safe threshold (lower = safer) + self.writer.add_scalar('Safety/ttc_violation_rate', ttc_violation_rate, self.episode_count) + + # Learning quality metrics (MARL paper-ready) + # Note: episode_length_ma removed - not useful for fixed simulation length + self.writer.add_scalar('Learning/reward_moving_avg', reward_ma, self.episode_count) + self.writer.add_scalar('Learning/reward_variance', reward_var, self.episode_count) + self.writer.add_scalar('Learning/reward_std', reward_std, self.episode_count) + self.writer.add_scalar('Learning/converged', 1.0 if self.is_converged else 0.0, self.episode_count) + + # Normalized coefficient of variation (for convergence visualization) + if reward_ma != 0: + cv = reward_std / abs(reward_ma) # Coefficient of variation + self.writer.add_scalar('Learning/reward_cv', cv, self.episode_count) + + # Traffic performance metrics (RA-L paper-ready) + if traffic_metrics: + # ============ KEY METRICS (most important for monitoring) ============ + # avg_speed: Actual average vehicle speed from CARLA (km/h) + if 'avg_speed' in traffic_metrics: + self.writer.add_scalar('Traffic/avg_speed', traffic_metrics['avg_speed'], self.episode_count) + + # target_speed_mean: What RL algorithm commanded vehicles to do (km/h) + if 'target_speed_mean' in traffic_metrics: + self.writer.add_scalar('Traffic/target_speed_mean', traffic_metrics['target_speed_mean'], self.episode_count) + + # speed_gap: Difference between commanded and actual speed (target - actual) + # Positive = vehicles slower than commanded, Negative = vehicles faster + avg_speed = traffic_metrics.get('avg_speed', 0) + target_speed = traffic_metrics.get('target_speed_mean', 0) + if target_speed > 0 and avg_speed > 0: + speed_gap = target_speed - avg_speed + self.writer.add_scalar('Traffic/speed_gap', speed_gap, self.episode_count) + + # ============ SECONDARY METRICS (for detailed analysis) ============ + # max_speed: Fastest actual vehicle speed in episode (km/h) + if 'max_speed' in traffic_metrics: + self.writer.add_scalar('Traffic/max_speed', traffic_metrics['max_speed'], self.episode_count) + # min_speed: Slowest actual vehicle speed in episode (km/h) + if 'min_speed' in traffic_metrics: + self.writer.add_scalar('Traffic/min_speed', traffic_metrics['min_speed'], self.episode_count) + # speed_std: Standard deviation of actual speeds (lower = more uniform) + if 'speed_std' in traffic_metrics: + self.writer.add_scalar('Traffic/speed_std', traffic_metrics['speed_std'], self.episode_count) + + # target_speed_max/min: Range of RL commanded speeds + if 'target_speed_max' in traffic_metrics: + self.writer.add_scalar('Traffic/target_speed_max', traffic_metrics['target_speed_max'], self.episode_count) + if 'target_speed_min' in traffic_metrics: + self.writer.add_scalar('Traffic/target_speed_min', traffic_metrics['target_speed_min'], self.episode_count) + # throughput: Vehicles completing per hour (success rate efficiency) + if 'throughput' in traffic_metrics: + self.writer.add_scalar('Traffic/throughput', traffic_metrics['throughput'], self.episode_count) + + # Log additional custom metrics + if additional_metrics: + for name, value in additional_metrics.items(): + self.writer.add_scalar(f'Episode/{name}', value, self.episode_count) + + # ============ IMPROVEMENT METRICS (for paper-ready visualization) ============ + # These show relative improvement over training, better for long training runs + self._log_improvement_metrics(success_rate, collision_rate, traffic_metrics) + + # Flush periodically (every 10 episodes) instead of every episode + # Reduces I/O blocking during training + if self.episode_count % 10 == 0: + self.writer.flush() + + def flush_tensorboard(self): + """Flush TensorBoard writer to ensure all metrics are written.""" + if self.writer is not None: + self.writer.flush() + + def close(self): + """Close TensorBoard writer and cleanup resources.""" + if self.writer is not None: + self.writer.close() + logger.info("TensorBoard writer closed") + + # ------------------------------------------------------------------ # + # Learning Quality Analysis Methods (for RA-L paper metrics) + # ------------------------------------------------------------------ # + + def _compute_reward_statistics(self) -> tuple: + """ + Compute reward moving average, variance, and standard deviation. + + Returns: + (moving_average, variance, std_deviation) + """ + if len(self.reward_history) == 0: + return 0.0, 0.0, 0.0 + + # Use last N episodes for moving average + window = list(self.reward_history)[-self.reward_window_size:] + reward_ma = float(np.mean(window)) + reward_var = float(np.var(window)) + reward_std = float(np.std(window)) + + return reward_ma, reward_var, reward_std + + def _compute_episode_length_ma(self) -> float: + """Compute moving average of episode lengths.""" + if len(self.episode_length_history) == 0: + return 0.0 + + window = list(self.episode_length_history)[-self.reward_window_size:] + return float(np.mean(window)) + + def _check_convergence(self): + """ + Check if training has converged based on reward and performance stability. + + MARL Traffic Convergence Criteria: + 1. Minimum episodes reached (default: 20) + 2. Reward CV (coefficient of variation) below threshold (default: 15%) + 3. Success rate stabilized (CV < 20%) + 4. Collision rate decreasing or stable + + This approach is more suitable for MARL traffic scenarios where: + - Early episodes have high variance due to exploration + - Success/collision rates are key performance indicators + """ + if self.is_converged: + return # Already converged + + # Need minimum episodes before checking convergence + if self.episode_count < self.min_episodes_for_convergence: + return + + if len(self.reward_history) < self.convergence_window: + return # Not enough data + + # Get recent metrics + recent_rewards = list(self.reward_history)[-self.convergence_window:] + mean_reward = np.mean(recent_rewards) + std_reward = np.std(recent_rewards) + + # Avoid division by zero + if abs(mean_reward) < 1e-8: + return + + # Coefficient of variation for rewards + reward_cv = std_reward / abs(mean_reward) + + # Check success rate stability (if tracked) + success_rate_stable = True + if len(self.success_rate_history) >= self.convergence_window: + recent_success = list(self.success_rate_history)[-self.convergence_window:] + mean_success = np.mean(recent_success) + if mean_success > 0.01: # Only check if there are meaningful successes + success_cv = np.std(recent_success) / mean_success + success_rate_stable = success_cv < 0.20 # 20% CV for success rate + + # Check collision rate trend (should be decreasing or stable) + collision_improving = True + if len(self.collision_rate_history) >= self.convergence_window: + recent_collision = list(self.collision_rate_history)[-self.convergence_window:] + # Compare first half to second half + first_half = np.mean(recent_collision[:self.convergence_window//2]) + second_half = np.mean(recent_collision[self.convergence_window//2:]) + collision_improving = second_half <= first_half * 1.1 # Allow 10% tolerance + + # Check convergence condition + reward_converged = reward_cv < self.convergence_threshold + if reward_converged and success_rate_stable and collision_improving: + self.is_converged = True + self.convergence_episode = self.episode_count + logger.success(f"Convergence detected at episode {self.episode_count}! " + f"Reward CV={reward_cv:.4f}, Success stable={success_rate_stable}, " + f"Collision improving={collision_improving}") + + def get_learning_statistics(self) -> Dict[str, Any]: + """ + Get comprehensive learning statistics for paper reporting. + + Returns: + Dictionary with learning quality metrics + """ + reward_ma, reward_var, reward_std = self._compute_reward_statistics() + length_ma = self._compute_episode_length_ma() + + stats = { + 'episode_count': self.episode_count, + 'reward_moving_avg': reward_ma, + 'reward_variance': reward_var, + 'reward_std': reward_std, + 'episode_length_ma': length_ma, + 'is_converged': self.is_converged, + 'convergence_episode': self.convergence_episode, + 'reward_history_size': len(self.reward_history), + } + + # Add coefficient of variation if mean is non-zero + if reward_ma != 0: + stats['coefficient_of_variation'] = reward_std / abs(reward_ma) + + # Add full reward history for plotting + if len(self.reward_history) > 0: + stats['reward_history'] = list(self.reward_history) + stats['episode_length_history'] = list(self.episode_length_history) + + return stats + + def _log_improvement_metrics(self, success_rate: float, collision_rate: float, + traffic_metrics: Dict[str, float] = None): + """ + Log improvement metrics for paper-ready visualization. + + These metrics show relative improvement over training, which is more meaningful + than raw values for long training runs (100K+ episodes). + + Metrics logged: + - Windowed moving averages (100-episode window) + - Delta from baseline (improvement from first 100 episodes) + - Percentage improvement from baseline + - Time-to-target tracking (episodes to reach X% success rate) + """ + if self.writer is None: + return + + window_size = 100 # Use 100-episode windows for smoother curves + + # Track success/collision rate moving averages with larger window + if len(self.success_rate_history) >= window_size: + # 100-episode moving average (smoother than 10-episode) + recent_success = list(self.success_rate_history)[-window_size:] + recent_collision = list(self.collision_rate_history)[-window_size:] + + success_ma_100 = float(np.mean(recent_success)) + collision_ma_100 = float(np.mean(recent_collision)) + + self.writer.add_scalar('Improvement/success_rate_ma100', success_ma_100, self.episode_count) + self.writer.add_scalar('Improvement/collision_rate_ma100', collision_ma_100, self.episode_count) + + # Calculate baseline from first 100 episodes + baseline_success = list(self.success_rate_history)[:window_size] + baseline_collision = list(self.collision_rate_history)[:window_size] + + baseline_success_avg = float(np.mean(baseline_success)) + baseline_collision_avg = float(np.mean(baseline_collision)) + + # Absolute improvement (delta from baseline) + success_delta = success_ma_100 - baseline_success_avg + collision_delta = collision_ma_100 - baseline_collision_avg + + self.writer.add_scalar('Improvement/success_rate_delta', success_delta, self.episode_count) + self.writer.add_scalar('Improvement/collision_rate_delta', collision_delta, self.episode_count) + + # Percentage improvement from baseline + # Formula: (current - baseline) / baseline * 100 + if baseline_success_avg > 1.0: # Avoid division by near-zero + success_improvement_pct = (success_ma_100 - baseline_success_avg) / baseline_success_avg * 100 + self.writer.add_scalar('Improvement/success_improvement_pct', success_improvement_pct, self.episode_count) + + if baseline_collision_avg > 1.0: + # For collision, negative improvement is good (reduction) + collision_reduction_pct = (baseline_collision_avg - collision_ma_100) / baseline_collision_avg * 100 + self.writer.add_scalar('Improvement/collision_reduction_pct', collision_reduction_pct, self.episode_count) + + # Track throughput improvement + if traffic_metrics and 'throughput' in traffic_metrics: + # Store throughput history for improvement calculation + if not hasattr(self, 'throughput_history'): + self.throughput_history = [] + self.throughput_history.append(traffic_metrics['throughput']) + + if len(self.throughput_history) >= window_size: + recent_throughput = self.throughput_history[-window_size:] + baseline_throughput = self.throughput_history[:window_size] + + throughput_ma_100 = float(np.mean(recent_throughput)) + baseline_throughput_avg = float(np.mean(baseline_throughput)) + + self.writer.add_scalar('Improvement/throughput_ma100', throughput_ma_100, self.episode_count) + + if baseline_throughput_avg > 0: + throughput_improvement_pct = (throughput_ma_100 - baseline_throughput_avg) / baseline_throughput_avg * 100 + self.writer.add_scalar('Improvement/throughput_improvement_pct', throughput_improvement_pct, self.episode_count) + + # Time-to-target tracking: log when key milestones are reached + # Track first episode to reach 70%, 80%, 90% success rate + if not hasattr(self, '_milestone_episodes'): + self._milestone_episodes = {70: None, 80: None, 90: None, 95: None} + + for target_pct, first_episode in self._milestone_episodes.items(): + if first_episode is None and success_rate >= target_pct: + self._milestone_episodes[target_pct] = self.episode_count + self.writer.add_scalar(f'Milestone/episodes_to_{target_pct}pct_success', + self.episode_count, self.episode_count) + logger.info(f"Milestone reached: {target_pct}% success rate at episode {self.episode_count}") + @abstractmethod def select_action(self, state: np.ndarray, training: bool = True) -> Any: """ diff --git a/opencda_marl/core/marl/algorithms/dqn.py b/opencda_marl/core/marl/algorithms/dqn.py index 4810f2d..2a4c569 100644 --- a/opencda_marl/core/marl/algorithms/dqn.py +++ b/opencda_marl/core/marl/algorithms/dqn.py @@ -11,6 +11,7 @@ import torch.optim as optim import numpy as np import random +import gc from collections import deque from typing import Dict, Any from loguru import logger @@ -206,7 +207,7 @@ def store_transition(self, state: np.ndarray, action: float, reward: float, def update(self) -> Dict[str, float]: """ Update Q-network using experience replay. - + Returns: Training metrics """ @@ -214,64 +215,132 @@ def update(self) -> Dict[str, float]: # Only update if we have enough samples if len(self.memory) < self.batch_size: return self.training_metrics - + # Sample batch transitions = self.memory.sample(self.batch_size) - + # Unpack batch states = torch.stack([t[0] for t in transitions]).to(self.device) actions = torch.LongTensor([t[1] for t in transitions]).to(self.device) rewards = torch.FloatTensor([t[2] for t in transitions]).to(self.device) next_states = torch.stack([t[3] for t in transitions]).to(self.device) dones = torch.BoolTensor([t[4] for t in transitions]).to(self.device) - + # Current Q values current_q_values = self.q_network(states).gather(1, actions.unsqueeze(1)) - + # Next Q values from target network with torch.no_grad(): next_q_values = self.target_network(next_states).max(1)[0] target_q_values = rewards + (self.discount_factor * next_q_values * ~dones) - + # Compute loss loss = F.mse_loss(current_q_values.squeeze(), target_q_values) - - # Optimize + + # Optimize with gradient clipping for stability self.optimizer.zero_grad() loss.backward() + + # Compute gradient norm BEFORE clipping (for monitoring) + grad_norm_pre = self._compute_grad_norm(self.q_network.parameters()) + + torch.nn.utils.clip_grad_norm_(self.q_network.parameters(), max_norm=1.0) + + # Compute gradient norm AFTER clipping + grad_norm_post = self._compute_grad_norm(self.q_network.parameters()) + self.optimizer.step() - + # Update target network periodically if self.training_step % self.target_update_freq == 0: self.target_network.load_state_dict(self.q_network.state_dict()) self.training_metrics['target_updates'] += 1 - + # Update epsilon self.epsilon = max(self.epsilon_min, self.epsilon * self.epsilon_decay) - - # Update metrics + + # Update metrics (compute before deleting tensors) with torch.no_grad(): q_mean = self.q_network(states).mean().item() - + loss_value = loss.item() + self.training_metrics.update({ - 'loss': loss.item(), + 'loss': loss_value, 'q_values_mean': q_mean, 'epsilon': self.epsilon, - 'memory_size': len(self.memory) + 'memory_size': len(self.memory), + 'grad_norm_pre': grad_norm_pre, + 'grad_norm_post': grad_norm_post }) - + + # TensorBoard logging (using base class methods) + self.log_scalar('Loss/dqn', loss_value, category='losses') + self.log_scalar('Q_values/mean', q_mean, category='q_values') + self.log_scalar('Exploration/epsilon', self.epsilon, category='episode') + self.log_scalar('Buffer/size', len(self.memory), category='buffer') + self.log_scalar('Gradients/q_network_pre_clip', grad_norm_pre, category='losses') + self.log_scalar('Gradients/q_network_post_clip', grad_norm_post, category='losses') + + # Explicit cleanup to prevent memory leaks (do this AFTER using tensors for metrics) + del states, actions, rewards, next_states, dones + del current_q_values, next_q_values, target_q_values + del transitions, loss + self.training_step += 1 - + + # Periodic deep cleanup to prevent CUDA memory fragmentation + # More aggressive: every 50 steps instead of 500 to prevent slowdown + if self.training_step % 50 == 0 and torch.cuda.is_available(): + torch.cuda.empty_cache() + gc.collect() + return self.training_metrics.copy() - + except Exception as e: logger.error(f"Error in DQN update: {e}") + # Cleanup even on error + if torch.cuda.is_available(): + torch.cuda.empty_cache() + gc.collect() return self.training_metrics.copy() def reset_episode(self): """Reset for new episode""" self.episode_count += 1 - + + # GPU memory cleanup to prevent slowdown over episodes + if torch.cuda.is_available(): + torch.cuda.empty_cache() + + # Log GPU memory usage periodically for debugging + if self.episode_count % 5 == 0: + allocated = torch.cuda.memory_allocated(self.device) / 1024**2 # MB + cached = torch.cuda.memory_reserved(self.device) / 1024**2 # MB + logger.debug(f"DQN Episode {self.episode_count} GPU memory: " + f"allocated={allocated:.1f}MB, cached={cached:.1f}MB") + + # Force garbage collection every few episodes to prevent memory leaks + if self.episode_count % 3 == 0: + gc.collect() + + def _compute_grad_norm(self, parameters) -> float: + """ + Compute the L2 norm of gradients for monitoring training stability. + Optimized: Single GPU-CPU sync instead of per-parameter sync. + + Args: + parameters: Iterator of model parameters + + Returns: + Total gradient norm (float) + """ + # Collect all gradients - avoid multiple .item() calls + grads = [p.grad.flatten() for p in parameters if p.grad is not None] + if not grads: + return 0.0 + # Single concatenation and norm computation on GPU, then one .item() + return torch.cat(grads).norm(2).item() + def get_training_info(self) -> Dict[str, Any]: """Get training information""" return { diff --git a/opencda_marl/core/marl/algorithms/mappo.py b/opencda_marl/core/marl/algorithms/mappo.py new file mode 100644 index 0000000..ae4729a --- /dev/null +++ b/opencda_marl/core/marl/algorithms/mappo.py @@ -0,0 +1,891 @@ +''' +Author : AXIBA leolihao@arizona.edu +Date : 2025-12-03 +FilePath : /OpenCDA-MARL/opencda_marl/core/marl/algorithms/mappo.py +Description : Multi-Agent Proximal Policy Optimization (MAPPO) algorithm + for cooperative autonomous intersection management. + +Reference : "The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games" + Yu et al., NeurIPS 2021 + GitHub: https://github.com/marlbenchmark/on-policy + + @article{yu2022surprising, + title={The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games}, + author={Yu, Chao and Velu, Akash and Vinitsky, Eugene and others}, + journal={Advances in Neural Information Processing Systems}, + volume={35}, + pages={24611--24624}, + year={2022} + } + +Copyright (c) 2025 by AXIBA (leolihao@arizona.edu), All Rights Reserved. +''' +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import numpy as np +import gc +from typing import Dict, Any, List, Tuple, Optional +from torch.distributions import Normal +from loguru import logger + +from .base_algorithm import BaseAlgorithm +from .rollout_buffer import RolloutBuffer + + +class GaussianActor(nn.Module): + """ + Gaussian policy network for continuous action space. + + Outputs mean and std for a Gaussian distribution over actions. + Uses learnable log_std parameter for stable training. + + Architecture follows MAPPO paper recommendations: + - Layer normalization for stability + - Orthogonal initialization for ReLU layers + - Small std initialization for output layer + + Parameters + ---------- + state_dim : int + Input observation dimension + action_dim : int + Output action dimension + hidden_dims : List[int] + Hidden layer dimensions + max_action : float + Maximum action value (for clamping) + min_action : float + Minimum action value + init_std : float + Initial standard deviation for exploration + """ + + def __init__(self, state_dim: int, action_dim: int, + hidden_dims: List[int] = [256, 256], + max_action: float = 65.0, min_action: float = 0.0, + init_std: float = 0.5): + super(GaussianActor, self).__init__() + + self.max_action = max_action + self.min_action = min_action + self.action_dim = action_dim + + # Build feature extraction network + layers = [] + in_dim = state_dim + for h_dim in hidden_dims: + layers.append(nn.Linear(in_dim, h_dim)) + layers.append(nn.LayerNorm(h_dim)) + layers.append(nn.ReLU()) + in_dim = h_dim + + self.feature_net = nn.Sequential(*layers) + + # Action mean head + self.action_mean = nn.Linear(hidden_dims[-1], action_dim) + + # Learnable log standard deviation (shared across all states) + # Initialize to produce init_std as initial standard deviation + self.action_log_std = nn.Parameter(torch.ones(action_dim) * np.log(init_std)) + + # Min/max log std to prevent numerical issues + self.log_std_min = np.log(0.01) # Min std = 0.01 + self.log_std_max = np.log(2.0) # Max std = 2.0 + + # Initialize weights properly + self._initialize_weights() + + def _initialize_weights(self): + """Initialize network weights for stable training.""" + # Initialize feature network with orthogonal initialization + for layer in self.feature_net: + if isinstance(layer, nn.Linear): + nn.init.orthogonal_(layer.weight, gain=np.sqrt(2)) + nn.init.constant_(layer.bias, 0.0) + + # Initialize action mean with small weights for controlled initial actions + nn.init.orthogonal_(self.action_mean.weight, gain=0.01) + # Bias initialized to produce middle-range speed (~40 km/h) + nn.init.constant_(self.action_mean.bias, 0.0) + + def forward(self, state: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Forward pass to get action distribution parameters. + + Parameters + ---------- + state : torch.Tensor + Input observation [batch_size, state_dim] + + Returns + ------- + action_mean : torch.Tensor + Mean of Gaussian distribution [batch_size, action_dim] + action_std : torch.Tensor + Standard deviation [batch_size, action_dim] + """ + features = self.feature_net(state) + action_mean = self.action_mean(features) + + # Clamp log_std for numerical stability + log_std = torch.clamp(self.action_log_std, self.log_std_min, self.log_std_max) + action_std = log_std.exp().expand_as(action_mean) + + return action_mean, action_std + + def get_action(self, state: torch.Tensor, deterministic: bool = False + ) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Sample action from policy. + + Parameters + ---------- + state : torch.Tensor + Current observation + deterministic : bool + If True, return mean action (no sampling) + + Returns + ------- + action : torch.Tensor + Sampled or deterministic action + log_prob : torch.Tensor + Log probability of action under current policy + """ + mean, std = self.forward(state) + + if deterministic: + # Return mean action (clamped to valid range) + action = torch.clamp(mean, self.min_action, self.max_action) + # Log prob of mean under the distribution + dist = Normal(mean, std) + log_prob = dist.log_prob(mean).sum(dim=-1) + return action, log_prob + + # Sample from Gaussian distribution + dist = Normal(mean, std) + action = dist.rsample() # Reparameterized sampling for gradients + + # Compute log probability (sum over action dimensions) + log_prob = dist.log_prob(action).sum(dim=-1) + + # Clamp action to valid range + action = torch.clamp(action, self.min_action, self.max_action) + + return action, log_prob + + def evaluate_actions(self, state: torch.Tensor, action: torch.Tensor + ) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Evaluate given actions under current policy. + + Used during PPO update to compute ratio between old and new policies. + + Parameters + ---------- + state : torch.Tensor + Batch of observations + action : torch.Tensor + Batch of actions to evaluate + + Returns + ------- + log_prob : torch.Tensor + Log probability of actions + entropy : torch.Tensor + Entropy of the action distribution + """ + mean, std = self.forward(state) + dist = Normal(mean, std) + + # Log probability of given actions + log_prob = dist.log_prob(action).sum(dim=-1) + + # Entropy (encourages exploration) + entropy = dist.entropy().sum(dim=-1) + + return log_prob, entropy + + +class CentralizedCritic(nn.Module): + """ + Centralized value function for MAPPO (CTDE paradigm). + + Takes global state (potentially all agents' observations) as input + and outputs a single value estimate. + + In our intersection scenario, the global state includes: + - Ego agent observation + - Multi-agent context from LSTM encoder + + Parameters + ---------- + global_state_dim : int + Dimension of global state input + hidden_dims : List[int] + Hidden layer dimensions + """ + + def __init__(self, global_state_dim: int, hidden_dims: List[int] = [256, 256]): + super(CentralizedCritic, self).__init__() + + # Build value network + layers = [] + in_dim = global_state_dim + for h_dim in hidden_dims: + layers.append(nn.Linear(in_dim, h_dim)) + layers.append(nn.LayerNorm(h_dim)) + layers.append(nn.ReLU()) + in_dim = h_dim + + # Final value output + layers.append(nn.Linear(hidden_dims[-1], 1)) + + self.value_net = nn.Sequential(*layers) + + # Initialize weights + self._initialize_weights() + + def _initialize_weights(self): + """Initialize network weights.""" + for layer in self.value_net: + if isinstance(layer, nn.Linear): + nn.init.orthogonal_(layer.weight, gain=np.sqrt(2)) + nn.init.constant_(layer.bias, 0.0) + + # Last layer with smaller gain for stable initial values + final_layer = self.value_net[-1] + nn.init.orthogonal_(final_layer.weight, gain=1.0) + + def forward(self, global_state: torch.Tensor) -> torch.Tensor: + """ + Compute state value. + + Parameters + ---------- + global_state : torch.Tensor + Global state observation [batch_size, global_state_dim] + + Returns + ------- + value : torch.Tensor + State value estimate [batch_size, 1] + """ + return self.value_net(global_state) + + +class LSTMEncoder(nn.Module): + """LSTM encoder for multi-agent context (same as TD3 for consistency).""" + + def __init__(self, input_size: int, hidden_size: int, num_layers: int = 1): + super(LSTMEncoder, self).__init__() + self.hidden_size = hidden_size + self.num_layers = num_layers + + self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True) + self._initialize_weights() + + def _initialize_weights(self): + """Initialize LSTM weights.""" + for name, param in self.lstm.named_parameters(): + if 'weight_ih' in name: + nn.init.xavier_uniform_(param) + elif 'weight_hh' in name: + nn.init.orthogonal_(param) + elif 'bias' in name: + nn.init.constant_(param, 0) + # Set forget gate bias to 1 + n = param.size(0) + with torch.no_grad(): + param.data[n//4:n//2] = 1.0 + + def forward(self, sequences: torch.Tensor) -> torch.Tensor: + """ + Encode sequences of agent observations. + + Parameters + ---------- + sequences : torch.Tensor + Agent observation sequences [batch_size, seq_len, input_size] + + Returns + ------- + encoding : torch.Tensor + Final hidden state [batch_size, hidden_size] + """ + batch_size = sequences.size(0) + h0 = torch.zeros(self.num_layers, batch_size, self.hidden_size, + device=sequences.device) + c0 = torch.zeros(self.num_layers, batch_size, self.hidden_size, + device=sequences.device) + + _, (hn, _) = self.lstm(sequences, (h0, c0)) + return hn[-1] # Return last layer's hidden state + + +class MAPPOAlgorithm(BaseAlgorithm): + """ + Multi-Agent Proximal Policy Optimization (MAPPO) algorithm. + + Implements the CTDE (Centralized Training, Decentralized Execution) paradigm: + - Training: Centralized critic sees global state (all agents) + - Execution: Decentralized actors use only local observations + + Key differences from TD3: + - On-policy: Experiences used once then discarded + - Stochastic policy: Gaussian distribution over actions + - PPO clipping: Prevents large policy updates + - Entropy bonus: Encourages exploration + + Reference + --------- + "The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games" + Yu et al., NeurIPS 2021 + GitHub: https://github.com/marlbenchmark/on-policy + + Parameters + ---------- + config : Dict[str, Any] + MAPPO configuration + state_dim : int + Individual agent observation dimension (44D in our case) + action_dim : int + Action dimension (1 for continuous speed control) + """ + + def __init__(self, config: Dict[str, Any], state_dim: int, action_dim: int): + super().__init__(config, state_dim, action_dim) + + # Device configuration + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + + # =========== MAPPO Hyperparameters =========== + # PPO clipping parameters + self.clip_param = config.get('clip_param', 0.2) + self.ppo_epochs = config.get('ppo_epochs', 10) + self.num_mini_batch = config.get('num_mini_batch', 4) + + # Loss coefficients + self.entropy_coef = config.get('entropy_coef', 0.01) + self.value_loss_coef = config.get('value_loss_coef', 0.5) + self.max_grad_norm = config.get('max_grad_norm', 0.5) + + # GAE parameters + self.gamma = config.get('gamma', 0.99) + self.gae_lambda = config.get('gae_lambda', 0.95) + self.use_gae = config.get('use_gae', True) + + # Learning rate + self.learning_rate = config.get('learning_rate', 3e-4) + + # Rollout settings + self.n_steps = config.get('n_steps', 128) # Steps before update + self.batch_size = config.get('batch_size', 32) + + # Action bounds (same as TD3) + self.max_action = config.get('max_action', 65.0) + self.min_action = config.get('min_action', 0.0) + + # Warmup configuration (NEW: random exploration before learning) + self.warmup_steps = config.get('warmup_steps', 0) + + # CTDE settings + self.use_centralized_V = config.get('use_centralized_V', True) + self.share_policy = config.get('share_policy', True) + + # =========== Network Architecture =========== + # LSTM encoder for multi-agent context + self.lstm_hidden_size = config.get('lstm_hidden_size', 256) + self.lstm_num_layers = config.get('lstm_num_layers', 1) + + self.lstm_encoder = LSTMEncoder( + input_size=state_dim, + hidden_size=self.lstm_hidden_size, + num_layers=self.lstm_num_layers + ).to(self.device) + + # Actor (Gaussian policy) + actor_hidden_dims = config.get('actor_hidden_dims', [256, 256]) + init_std = config.get('init_std', 0.5) + + self.actor = GaussianActor( + state_dim=state_dim, + action_dim=action_dim, + hidden_dims=actor_hidden_dims, + max_action=self.max_action, + min_action=self.min_action, + init_std=init_std + ).to(self.device) + + # Critic (centralized value function) + # Input: ego state + LSTM context + critic_input_dim = state_dim + self.lstm_hidden_size + critic_hidden_dims = config.get('critic_hidden_dims', [256, 256]) + + self.critic = CentralizedCritic( + global_state_dim=critic_input_dim, + hidden_dims=critic_hidden_dims + ).to(self.device) + + # =========== Optimizers =========== + # Single optimizer for all networks (MAPPO paper recommendation) + self.optimizer = optim.Adam([ + {'params': self.actor.parameters()}, + {'params': self.critic.parameters()}, + {'params': self.lstm_encoder.parameters()} + ], lr=self.learning_rate) + + # Learning rate scheduler (optional) + self.use_lr_decay = config.get('use_lr_decay', False) + if self.use_lr_decay: + self.lr_scheduler = optim.lr_scheduler.LinearLR( + self.optimizer, start_factor=1.0, end_factor=0.1, total_iters=1000 + ) + + # =========== Rollout Buffer =========== + self.rollout_buffer = RolloutBuffer( + buffer_size=self.n_steps * 4, # Allow for multiple agents + state_dim=state_dim, + action_dim=action_dim, + gamma=self.gamma, + gae_lambda=self.gae_lambda, + device=self.device + ) + + # =========== Training State =========== + self.training = True + self.steps_collected = 0 + self.updates_performed = 0 + self._pretrained = False + + # Track last action per agent for store_transition + self.last_log_probs: Dict[str, float] = {} + self.last_values: Dict[str, float] = {} + + # Training metrics + self.training_metrics = { + 'policy_loss': 0.0, + 'value_loss': 0.0, + 'entropy': 0.0, + 'approx_kl': 0.0, + 'clip_fraction': 0.0, + 'buffer_size': 0, + } + + logger.info(f"MAPPO initialized: state_dim={state_dim}, action_dim={action_dim}, " + f"device={self.device}") + logger.info(f"MAPPO hyperparameters: clip={self.clip_param}, epochs={self.ppo_epochs}, " + f"entropy_coef={self.entropy_coef}, warmup_steps={self.warmup_steps}") + + def select_action(self, multi_agent_obs: Dict[str, np.ndarray], + ego_agent_id: str, training: bool = True) -> Optional[float]: + """ + Select action for ego agent using MAPPO policy. + + Parameters + ---------- + multi_agent_obs : Dict[str, np.ndarray] + All agents' observations + ego_agent_id : str + ID of the ego agent + training : bool + Whether in training mode + + Returns + ------- + action : float or None + Target speed in km/h, or None during warmup + """ + try: + # Warmup phase: random exploration before policy learning + if training and self.warmup_steps > 0 and self.steps_collected < self.warmup_steps: + # Random action during warmup for exploration + random_speed = np.random.uniform(self.min_action, self.max_action) + + # Still need to store log_prob and value for transition + self.last_log_probs[ego_agent_id] = 0.0 # Placeholder + self.last_values[ego_agent_id] = 0.0 # Placeholder + + if self.steps_collected % 200 == 0: + logger.debug(f"MAPPO Warmup: step {self.steps_collected}/{self.warmup_steps}, " + f"random_speed={random_speed:.1f}") + + return random_speed + + # Prepare inputs + ego_state, multi_agent_context = self._prepare_inputs( + multi_agent_obs, ego_agent_id + ) + + with torch.no_grad(): + # Get action from actor + if training: + action, log_prob = self.actor.get_action(ego_state, deterministic=False) + else: + action, log_prob = self.actor.get_action(ego_state, deterministic=True) + + # Get value estimate from critic + critic_input = torch.cat([ego_state, multi_agent_context], dim=1) + value = self.critic(critic_input) + + # Store for later transition storage + self.last_log_probs[ego_agent_id] = log_prob.item() + self.last_values[ego_agent_id] = value.item() + + # Extract scalar action + final_speed = action.squeeze().item() + final_speed = np.clip(final_speed, self.min_action, self.max_action) + + # Debug logging (periodic) + if self.steps_collected % 1000 == 0: + mean, std = self.actor.forward(ego_state) + logger.debug(f"MAPPO Action: speed={final_speed:.1f}, " + f"mean={mean.item():.1f}, std={std.item():.2f}, " + f"value={value.item():.2f}") + + return final_speed + + except Exception as e: + logger.error(f"Error in MAPPO action selection: {e}") + import traceback + traceback.print_exc() + return None + + def store_transition(self, multi_agent_obs: Dict[str, np.ndarray], + ego_agent_id: str, action: float, reward: float, + next_multi_agent_obs: Dict[str, np.ndarray], done: bool): + """ + Store transition in rollout buffer. + + Parameters + ---------- + multi_agent_obs : Dict[str, np.ndarray] + Current observations + ego_agent_id : str + Ego agent ID + action : float + Action taken (speed) + reward : float + Reward received + next_multi_agent_obs : Dict[str, np.ndarray] + Next observations + done : bool + Episode termination flag + """ + try: + # Get stored log_prob and value from action selection + log_prob = self.last_log_probs.get(ego_agent_id, 0.0) + value = self.last_values.get(ego_agent_id, 0.0) + + # Get ego observation + if ego_agent_id in multi_agent_obs: + ego_obs = multi_agent_obs[ego_agent_id] + else: + ego_obs = list(multi_agent_obs.values())[0] + + # Add to buffer + self.rollout_buffer.add( + obs=ego_obs, + action=action, + reward=reward, + value=value, + log_prob=log_prob, + done=done + ) + + self.steps_collected += 1 + + except Exception as e: + logger.error(f"Error storing MAPPO transition: {e}") + + def update(self) -> Dict[str, float]: + """ + Perform PPO update if enough data collected. + + Returns + ------- + metrics : Dict[str, float] + Training metrics from update + """ + # Skip updates during warmup phase + if self.warmup_steps > 0 and self.steps_collected < self.warmup_steps: + return self.training_metrics + + # Check if enough data for update + if len(self.rollout_buffer) < self.batch_size: + return self.training_metrics + + try: + # Compute returns and advantages using GAE + self.rollout_buffer.compute_returns_and_advantages( + last_value=0.0, # Assume terminal (or use bootstrap) + normalize_advantages=True + ) + + # PPO update epochs + total_policy_loss = 0.0 + total_value_loss = 0.0 + total_entropy = 0.0 + total_approx_kl = 0.0 + total_clip_fraction = 0.0 + num_updates = 0 + + for epoch in range(self.ppo_epochs): + # Get mini-batches + batch_size = max(len(self.rollout_buffer) // self.num_mini_batch, 1) + + for batch in self.rollout_buffer.get_batches(batch_size=batch_size): + # Unpack batch + obs = batch['observations'] + actions = batch['actions'].unsqueeze(-1) # [batch, 1] + old_log_probs = batch['old_log_probs'] + advantages = batch['advantages'] + returns = batch['returns'] + + # Evaluate current policy + new_log_probs, entropy = self.actor.evaluate_actions(obs, actions) + + # Compute critic values + # For simplicity, use ego obs directly (no LSTM in this batch) + # In full implementation, would need to re-encode multi-agent context + critic_input = torch.cat([ + obs, + torch.zeros(obs.size(0), self.lstm_hidden_size, device=self.device) + ], dim=1) + values = self.critic(critic_input).squeeze(-1) + + # PPO policy loss with clipping + ratio = (new_log_probs - old_log_probs).exp() + surr1 = ratio * advantages + surr2 = torch.clamp( + ratio, 1.0 - self.clip_param, 1.0 + self.clip_param + ) * advantages + policy_loss = -torch.min(surr1, surr2).mean() + + # Value loss + value_loss = F.mse_loss(values, returns) + + # Entropy bonus (negative because we want to maximize entropy) + entropy_loss = -entropy.mean() + + # Combined loss + loss = (policy_loss + + self.value_loss_coef * value_loss + + self.entropy_coef * entropy_loss) + + # Optimization step + self.optimizer.zero_grad() + loss.backward() + nn.utils.clip_grad_norm_( + list(self.actor.parameters()) + + list(self.critic.parameters()) + + list(self.lstm_encoder.parameters()), + self.max_grad_norm + ) + self.optimizer.step() + + # Track metrics + total_policy_loss += policy_loss.item() + total_value_loss += value_loss.item() + total_entropy += entropy.mean().item() + + # Approximate KL divergence + with torch.no_grad(): + approx_kl = ((ratio - 1) - (ratio.log())).mean().item() + clip_fraction = ((ratio - 1.0).abs() > self.clip_param).float().mean().item() + + total_approx_kl += approx_kl + total_clip_fraction += clip_fraction + num_updates += 1 + + # Average metrics + if num_updates > 0: + self.training_metrics = { + 'policy_loss': total_policy_loss / num_updates, + 'value_loss': total_value_loss / num_updates, + 'entropy': total_entropy / num_updates, + 'approx_kl': total_approx_kl / num_updates, + 'clip_fraction': total_clip_fraction / num_updates, + 'buffer_size': len(self.rollout_buffer), + } + + # Clear buffer (on-policy requirement) + self.rollout_buffer.clear() + self.updates_performed += 1 + + # LR decay if enabled + if self.use_lr_decay: + self.lr_scheduler.step() + + # TensorBoard logging + self.log_scalar('Loss/policy', self.training_metrics['policy_loss'], category='losses') + self.log_scalar('Loss/value', self.training_metrics['value_loss'], category='losses') + self.log_scalar('Policy/entropy', self.training_metrics['entropy'], category='losses') + self.log_scalar('Policy/approx_kl', self.training_metrics['approx_kl'], category='losses') + self.log_scalar('Policy/clip_fraction', self.training_metrics['clip_fraction'], category='losses') + + # Log progress periodically + if self.updates_performed % 10 == 0: + logger.info(f"MAPPO Update {self.updates_performed}: " + f"policy_loss={self.training_metrics['policy_loss']:.4f}, " + f"value_loss={self.training_metrics['value_loss']:.4f}, " + f"entropy={self.training_metrics['entropy']:.4f}") + + # GPU cleanup + if torch.cuda.is_available() and self.updates_performed % 10 == 0: + torch.cuda.empty_cache() + gc.collect() + + return self.training_metrics + + except Exception as e: + logger.error(f"Error in MAPPO update: {e}") + import traceback + traceback.print_exc() + return self.training_metrics + + def _prepare_inputs(self, multi_agent_obs: Dict[str, np.ndarray], + ego_agent_id: str) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Prepare inputs for MAPPO networks. + + Returns + ------- + ego_state : torch.Tensor + Ego agent features [1, state_dim] + multi_agent_context : torch.Tensor + LSTM encoding [1, lstm_hidden_size] + """ + # Get ego observation + if ego_agent_id not in multi_agent_obs: + ego_agent_id = list(multi_agent_obs.keys())[0] + + ego_obs = multi_agent_obs[ego_agent_id] + ego_state = torch.FloatTensor(ego_obs).unsqueeze(0).to(self.device) + + # Prepare agent sequence for LSTM + agent_sequence = [ego_obs] + + # Add other agents sorted by distance + other_agents = [] + for agent_id, obs in multi_agent_obs.items(): + if agent_id != ego_agent_id: + rel_x, rel_y = obs[0], obs[1] + distance = np.sqrt(rel_x*rel_x + rel_y*rel_y) + other_agents.append((obs, distance)) + + other_agents.sort(key=lambda x: x[1]) + for obs, _ in other_agents: + agent_sequence.append(obs) + + # Encode with LSTM + if len(agent_sequence) > 1: + sequence_array = np.array(agent_sequence) + sequence_tensor = torch.FloatTensor(sequence_array).unsqueeze(0).to(self.device) + multi_agent_context = self.lstm_encoder(sequence_tensor) + else: + multi_agent_context = torch.zeros(1, self.lstm_hidden_size).to(self.device) + + return ego_state, multi_agent_context + + def reset_episode(self): + """Reset for new episode.""" + self.episode_count += 1 + self.last_log_probs.clear() + self.last_values.clear() + + # Log to TensorBoard + if self.writer is not None: + self.writer.add_scalar('MAPPO/steps_collected', self.steps_collected, self.episode_count) + self.writer.add_scalar('MAPPO/updates_performed', self.updates_performed, self.episode_count) + + # GPU cleanup every few episodes + if self.episode_count % 5 == 0 and torch.cuda.is_available(): + torch.cuda.empty_cache() + gc.collect() + + def get_training_info(self) -> Dict[str, Any]: + """Get training information.""" + return { + 'algorithm': 'MAPPO', + 'algorithm_type': 'multi_agent_ppo', + 'episode_count': self.episode_count, + 'state_dim': self.state_dim, + 'action_dim': self.action_dim, + 'buffer_size': len(self.rollout_buffer), + 'steps_collected': self.steps_collected, + 'updates_performed': self.updates_performed, + 'clip_param': self.clip_param, + 'entropy_coef': self.entropy_coef, + 'warmup_steps': self.warmup_steps, + 'in_warmup': self.steps_collected < self.warmup_steps if self.warmup_steps > 0 else False, + 'policy_loss': self.training_metrics.get('policy_loss', 0.0), + 'value_loss': self.training_metrics.get('value_loss', 0.0), + 'entropy': self.training_metrics.get('entropy', 0.0), + 'epsilon': self.entropy_coef, # For GUI compatibility + 'device': str(self.device), + 'max_action': self.max_action, + } + + def log_episode_metrics(self, episode_reward: float, episode_length: int, + success_rate: float = 0.0, collision_rate: float = 0.0, + near_miss_count: int = 0, ttc_violation_rate: float = 0.0, + additional_metrics: Dict[str, float] = None, + traffic_metrics: Dict[str, float] = None): + """Log episode-level metrics to TensorBoard.""" + # Add MAPPO-specific metrics + mappo_metrics = { + 'buffer_size': len(self.rollout_buffer), + 'updates_performed': self.updates_performed, + } + if additional_metrics: + mappo_metrics.update(additional_metrics) + + # Call base class method + super().log_episode_metrics( + episode_reward, episode_length, success_rate, collision_rate, + near_miss_count=near_miss_count, + ttc_violation_rate=ttc_violation_rate, + additional_metrics=mappo_metrics, + traffic_metrics=traffic_metrics + ) + + def save(self, path: str): + """Save MAPPO model.""" + try: + save_data = { + 'actor_state_dict': self.actor.state_dict(), + 'critic_state_dict': self.critic.state_dict(), + 'lstm_encoder_state_dict': self.lstm_encoder.state_dict(), + 'optimizer_state_dict': self.optimizer.state_dict(), + 'episode_count': self.episode_count, + 'updates_performed': self.updates_performed, + 'training_metrics': self.training_metrics, + 'config': self.config, + } + torch.save(save_data, path) + logger.info(f"MAPPO model saved to {path}") + except Exception as e: + logger.error(f"Error saving MAPPO model: {e}") + + def load(self, path: str): + """Load MAPPO model.""" + try: + save_data = torch.load(path, map_location=self.device, weights_only=False) + + self.actor.load_state_dict(save_data['actor_state_dict']) + self.critic.load_state_dict(save_data['critic_state_dict']) + self.lstm_encoder.load_state_dict(save_data['lstm_encoder_state_dict']) + self.optimizer.load_state_dict(save_data['optimizer_state_dict']) + self.episode_count = save_data.get('episode_count', 0) + self.updates_performed = save_data.get('updates_performed', 0) + + if 'training_metrics' in save_data: + self.training_metrics = save_data['training_metrics'] + + self._pretrained = True + logger.info(f"MAPPO model loaded from {path}") + except Exception as e: + logger.error(f"Error loading MAPPO model: {e}") diff --git a/opencda_marl/core/marl/algorithms/q_learning.py b/opencda_marl/core/marl/algorithms/q_learning.py index 3cb0291..46543a4 100644 --- a/opencda_marl/core/marl/algorithms/q_learning.py +++ b/opencda_marl/core/marl/algorithms/q_learning.py @@ -166,13 +166,25 @@ def update(self) -> Dict[str, float]: self.epsilon_min, self.epsilon * self.epsilon_decay) # Update metrics + q_mean = float(np.mean(self.q_table)) + q_std = float(np.std(self.q_table)) + nonzero_q = int(np.count_nonzero(self.q_table)) + self.training_metrics.update({ - 'q_values_mean': float(np.mean(self.q_table)), - 'q_values_std': float(np.std(self.q_table)), + 'q_values_mean': q_mean, + 'q_values_std': q_std, 'epsilon': self.epsilon, - 'nonzero_q_values': int(np.count_nonzero(self.q_table)) + 'nonzero_q_values': nonzero_q }) + # TensorBoard logging (using base class methods) + self.log_scalar('Q_values/mean', q_mean, category='q_values') + self.log_scalar('Q_values/std', q_std, category='q_values') + self.log_scalar('Q_values/nonzero_count', nonzero_q, category='q_values') + self.log_scalar('Exploration/epsilon', self.epsilon, category='episode') + if self.use_experience_replay: + self.log_scalar('Buffer/size', len(self.memory), category='buffer') + self.training_step += 1 return self.training_metrics.copy() diff --git a/opencda_marl/core/marl/algorithms/rollout_buffer.py b/opencda_marl/core/marl/algorithms/rollout_buffer.py new file mode 100644 index 0000000..8a354f9 --- /dev/null +++ b/opencda_marl/core/marl/algorithms/rollout_buffer.py @@ -0,0 +1,448 @@ +''' +Author : AXIBA leolihao@arizona.edu +Date : 2025-12-03 +FilePath : /OpenCDA-MARL/opencda_marl/core/marl/algorithms/rollout_buffer.py +Description : Rollout buffer for on-policy algorithms (MAPPO, PPO) + Implements GAE (Generalized Advantage Estimation) for advantage computation. + +Reference : "The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games" + GitHub: https://github.com/marlbenchmark/on-policy + +Copyright (c) 2025 by AXIBA (leolihao@arizona.edu), All Rights Reserved. +''' +import torch +import numpy as np +from typing import Dict, List, Optional, Generator +from loguru import logger + + +class RolloutBuffer: + """ + On-policy rollout buffer for MAPPO/PPO algorithms. + + Stores trajectories and computes GAE (Generalized Advantage Estimation) + for policy gradient updates. Unlike off-policy buffers, this buffer is + cleared after each policy update. + + Key differences from TD3's replay buffer: + - On-policy: Data used only once per update, then cleared + - Stores log_probs and values for PPO's clipped objective + - Computes advantages using GAE for variance reduction + + Parameters + ---------- + buffer_size : int + Maximum number of transitions to store per rollout + state_dim : int + Dimension of the observation space + action_dim : int + Dimension of the action space + gamma : float + Discount factor for rewards + gae_lambda : float + GAE lambda for advantage estimation (tradeoff between bias and variance) + device : torch.device + Device to store tensors on + """ + + def __init__(self, buffer_size: int, state_dim: int, action_dim: int, + gamma: float = 0.99, gae_lambda: float = 0.95, + device: torch.device = None): + self.buffer_size = buffer_size + self.state_dim = state_dim + self.action_dim = action_dim + self.gamma = gamma + self.gae_lambda = gae_lambda + self.device = device or torch.device("cuda" if torch.cuda.is_available() else "cpu") + + # Storage lists (reset after each update) + self.observations: List[np.ndarray] = [] + self.actions: List[float] = [] + self.rewards: List[float] = [] + self.values: List[float] = [] + self.log_probs: List[float] = [] + self.dones: List[bool] = [] + + # Multi-agent context storage (for LSTM encoder if used) + self.multi_agent_contexts: List[np.ndarray] = [] + + # Computed after rollout collection + self.advantages: Optional[torch.Tensor] = None + self.returns: Optional[torch.Tensor] = None + + # Statistics tracking + self.total_transitions_stored = 0 + self.num_rollouts = 0 + + def add(self, obs: np.ndarray, action: float, reward: float, + value: float, log_prob: float, done: bool, + multi_agent_context: np.ndarray = None): + """ + Add a single transition to the buffer. + + Parameters + ---------- + obs : np.ndarray + Current observation + action : float + Action taken + reward : float + Reward received + value : float + Value estimate V(s) from critic + log_prob : float + Log probability of action under current policy + done : bool + Whether episode terminated + multi_agent_context : np.ndarray, optional + LSTM encoding of multi-agent context (for centralized critic) + """ + self.observations.append(obs) + self.actions.append(action) + self.rewards.append(reward) + self.values.append(value) + self.log_probs.append(log_prob) + self.dones.append(done) + + if multi_agent_context is not None: + self.multi_agent_contexts.append(multi_agent_context) + + self.total_transitions_stored += 1 + + def compute_returns_and_advantages(self, last_value: float = 0.0, + normalize_advantages: bool = True): + """ + Compute GAE advantages and returns for collected trajectories. + + Uses Generalized Advantage Estimation (GAE): + A_t = δ_t + (γλ)δ_{t+1} + ... + (γλ)^{T-t+1}δ_{T-1} + where δ_t = r_t + γV(s_{t+1}) - V(s_t) + + Parameters + ---------- + last_value : float + Value estimate for the last state (bootstrap value) + normalize_advantages : bool + Whether to normalize advantages to zero mean, unit variance + """ + if len(self.rewards) == 0: + logger.warning("RolloutBuffer: No transitions to compute returns") + return + + # Convert to numpy for efficient computation + rewards = np.array(self.rewards, dtype=np.float32) + values = np.array(self.values, dtype=np.float32) + dones = np.array(self.dones, dtype=np.float32) + + # Compute GAE advantages + advantages = np.zeros_like(rewards) + gae = 0.0 + + for t in reversed(range(len(rewards))): + if t == len(rewards) - 1: + next_value = last_value + next_non_terminal = 1.0 - float(dones[t]) + else: + next_value = values[t + 1] + next_non_terminal = 1.0 - dones[t] + + # TD error: δ_t = r_t + γV(s_{t+1}) - V(s_t) + delta = rewards[t] + self.gamma * next_value * next_non_terminal - values[t] + + # GAE: A_t = δ_t + γλA_{t+1} + gae = delta + self.gamma * self.gae_lambda * next_non_terminal * gae + advantages[t] = gae + + # Returns = advantages + values (used for value function fitting) + returns = advantages + values + + # Convert to tensors + self.advantages = torch.from_numpy(advantages).float().to(self.device) + self.returns = torch.from_numpy(returns).float().to(self.device) + + # Normalize advantages for stable training + if normalize_advantages and len(advantages) > 1: + self.advantages = (self.advantages - self.advantages.mean()) / (self.advantages.std() + 1e-8) + + logger.debug(f"GAE computed: advantages mean={self.advantages.mean():.4f}, " + f"std={self.advantages.std():.4f}, returns mean={self.returns.mean():.4f}") + + def get_batches(self, batch_size: int = None, shuffle: bool = True) -> Generator: + """ + Generate mini-batches for PPO updates. + + Parameters + ---------- + batch_size : int, optional + Size of each mini-batch (default: use all data) + shuffle : bool + Whether to shuffle data before batching + + Yields + ------ + Dict containing batch of: + - observations, actions, old_log_probs, advantages, returns + - multi_agent_contexts (if available) + """ + if self.advantages is None or self.returns is None: + raise RuntimeError("Must call compute_returns_and_advantages before get_batches") + + n_samples = len(self.observations) + if batch_size is None or batch_size >= n_samples: + batch_size = n_samples + + # Convert all data to tensors + obs_tensor = torch.FloatTensor(np.stack(self.observations)).to(self.device) + actions_tensor = torch.FloatTensor(self.actions).to(self.device) + old_log_probs_tensor = torch.FloatTensor(self.log_probs).to(self.device) + + # Multi-agent contexts if available + contexts_tensor = None + if len(self.multi_agent_contexts) > 0: + contexts_tensor = torch.FloatTensor(np.stack(self.multi_agent_contexts)).to(self.device) + + # Generate batch indices + indices = np.arange(n_samples) + if shuffle: + np.random.shuffle(indices) + + # Yield batches + for start_idx in range(0, n_samples, batch_size): + end_idx = min(start_idx + batch_size, n_samples) + batch_indices = indices[start_idx:end_idx] + + batch = { + 'observations': obs_tensor[batch_indices], + 'actions': actions_tensor[batch_indices], + 'old_log_probs': old_log_probs_tensor[batch_indices], + 'advantages': self.advantages[batch_indices], + 'returns': self.returns[batch_indices], + } + + if contexts_tensor is not None: + batch['multi_agent_contexts'] = contexts_tensor[batch_indices] + + yield batch + + def clear(self): + """ + Clear the buffer after policy update (on-policy requirement). + + Unlike off-policy algorithms that reuse experiences, on-policy + methods like MAPPO must clear old data after each update. + """ + self.observations.clear() + self.actions.clear() + self.rewards.clear() + self.values.clear() + self.log_probs.clear() + self.dones.clear() + self.multi_agent_contexts.clear() + + self.advantages = None + self.returns = None + + self.num_rollouts += 1 + + def __len__(self) -> int: + """Return number of transitions in buffer.""" + return len(self.observations) + + def is_full(self) -> bool: + """Check if buffer has reached capacity.""" + return len(self.observations) >= self.buffer_size + + def get_stats(self) -> Dict: + """Get buffer statistics for debugging.""" + return { + 'current_size': len(self.observations), + 'buffer_capacity': self.buffer_size, + 'total_stored': self.total_transitions_stored, + 'num_rollouts': self.num_rollouts, + 'has_advantages': self.advantages is not None, + 'has_returns': self.returns is not None, + } + + +class MultiAgentRolloutBuffer: + """ + Rollout buffer for multi-agent scenarios. + + Maintains separate buffers for each agent while supporting + centralized critic (which sees all agents' observations). + + This is specifically designed for MAPPO's CTDE paradigm: + - Centralized Training: Critic sees global state + - Decentralized Execution: Actor uses local observation + + Parameters + ---------- + buffer_size : int + Maximum transitions per agent per rollout + state_dim : int + Individual agent observation dimension + action_dim : int + Action dimension (shared across agents) + max_agents : int + Maximum number of concurrent agents + gamma : float + Discount factor + gae_lambda : float + GAE lambda + device : torch.device + Compute device + """ + + def __init__(self, buffer_size: int, state_dim: int, action_dim: int, + max_agents: int = 4, gamma: float = 0.99, + gae_lambda: float = 0.95, device: torch.device = None): + self.buffer_size = buffer_size + self.state_dim = state_dim + self.action_dim = action_dim + self.max_agents = max_agents + self.gamma = gamma + self.gae_lambda = gae_lambda + self.device = device or torch.device("cuda" if torch.cuda.is_available() else "cpu") + + # Per-agent buffers + self.agent_buffers: Dict[str, RolloutBuffer] = {} + + # Global state storage for centralized critic + self.global_states: List[np.ndarray] = [] + + def get_agent_buffer(self, agent_id: str) -> RolloutBuffer: + """Get or create buffer for specific agent.""" + if agent_id not in self.agent_buffers: + self.agent_buffers[agent_id] = RolloutBuffer( + buffer_size=self.buffer_size, + state_dim=self.state_dim, + action_dim=self.action_dim, + gamma=self.gamma, + gae_lambda=self.gae_lambda, + device=self.device + ) + return self.agent_buffers[agent_id] + + def add(self, agent_id: str, obs: np.ndarray, action: float, + reward: float, value: float, log_prob: float, done: bool, + global_state: np.ndarray = None): + """ + Add transition for specific agent. + + Parameters + ---------- + agent_id : str + Agent identifier + obs : np.ndarray + Agent's local observation + action : float + Action taken + reward : float + Reward received + value : float + Value estimate from centralized critic + log_prob : float + Log probability under current policy + done : bool + Whether agent's episode terminated + global_state : np.ndarray, optional + Global state for centralized critic (all agents' observations) + """ + buffer = self.get_agent_buffer(agent_id) + buffer.add(obs, action, reward, value, log_prob, done) + + if global_state is not None: + self.global_states.append(global_state) + + def compute_all_returns_and_advantages(self, last_values: Dict[str, float] = None, + normalize: bool = True): + """ + Compute returns and advantages for all agents. + + Parameters + ---------- + last_values : Dict[str, float], optional + Bootstrap values for each agent + normalize : bool + Whether to normalize advantages + """ + if last_values is None: + last_values = {} + + for agent_id, buffer in self.agent_buffers.items(): + last_value = last_values.get(agent_id, 0.0) + buffer.compute_returns_and_advantages(last_value, normalize) + + def get_combined_batches(self, batch_size: int = None) -> Generator: + """ + Get batches combining all agents' experiences. + + Useful for shared policy training where all agents share parameters. + + Yields + ------ + Dict with combined batch data from all agents + """ + # Combine all agent data + all_obs = [] + all_actions = [] + all_log_probs = [] + all_advantages = [] + all_returns = [] + + for agent_id, buffer in self.agent_buffers.items(): + if buffer.advantages is None: + continue + + all_obs.extend(buffer.observations) + all_actions.extend(buffer.actions) + all_log_probs.extend(buffer.log_probs) + all_advantages.append(buffer.advantages) + all_returns.append(buffer.returns) + + if len(all_advantages) == 0: + return + + # Concatenate tensors + obs_tensor = torch.FloatTensor(np.stack(all_obs)).to(self.device) + actions_tensor = torch.FloatTensor(all_actions).to(self.device) + log_probs_tensor = torch.FloatTensor(all_log_probs).to(self.device) + advantages_tensor = torch.cat(all_advantages) + returns_tensor = torch.cat(all_returns) + + n_samples = len(obs_tensor) + if batch_size is None or batch_size >= n_samples: + batch_size = n_samples + + indices = np.arange(n_samples) + np.random.shuffle(indices) + + for start_idx in range(0, n_samples, batch_size): + end_idx = min(start_idx + batch_size, n_samples) + batch_indices = indices[start_idx:end_idx] + + yield { + 'observations': obs_tensor[batch_indices], + 'actions': actions_tensor[batch_indices], + 'old_log_probs': log_probs_tensor[batch_indices], + 'advantages': advantages_tensor[batch_indices], + 'returns': returns_tensor[batch_indices], + } + + def clear(self): + """Clear all agent buffers.""" + for buffer in self.agent_buffers.values(): + buffer.clear() + self.global_states.clear() + + def total_transitions(self) -> int: + """Get total transitions across all agents.""" + return sum(len(b) for b in self.agent_buffers.values()) + + def get_stats(self) -> Dict: + """Get combined statistics.""" + return { + 'num_agents': len(self.agent_buffers), + 'total_transitions': self.total_transitions(), + 'per_agent': {aid: b.get_stats() for aid, b in self.agent_buffers.items()} + } diff --git a/opencda_marl/core/marl/algorithms/sac.py b/opencda_marl/core/marl/algorithms/sac.py new file mode 100644 index 0000000..37c0d63 --- /dev/null +++ b/opencda_marl/core/marl/algorithms/sac.py @@ -0,0 +1,850 @@ +''' +Author : AXIBA leolihao@arizona.edu +Date : 2025-12-05 +FilePath : /OpenCDA-MARL/opencda_marl/core/marl/algorithms/sac.py +Description : Soft Actor-Critic (SAC) algorithm with auto-tuning entropy + for multi-agent intersection control. + +Reference : "Soft Actor-Critic: Off-Policy Maximum Entropy Deep Reinforcement Learning + with a Stochastic Actor" - Haarnoja et al., ICML 2018 (arXiv:1801.01290) + "Soft Actor-Critic Algorithms and Applications" - Haarnoja et al., 2018 + (arXiv:1812.05905) + +Copyright (c) 2025 by AXIBA (leolihao@arizona.edu), All Rights Reserved. +''' +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import numpy as np +import gc +from typing import Dict, Any, List, Tuple, Optional +from torch.distributions import Normal +from loguru import logger + +from .base_algorithm import BaseAlgorithm +from .smart_replay_buffer import SmartReplayBuffer + + +class LSTMEncoder(nn.Module): + """LSTM encoder for multi-agent context (shared with TD3/MAPPO).""" + + def __init__(self, input_size: int, hidden_size: int, num_layers: int = 1): + super(LSTMEncoder, self).__init__() + self.hidden_size = hidden_size + self.num_layers = num_layers + + self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True) + self._initialize_weights() + + def _initialize_weights(self): + """Initialize LSTM weights.""" + for name, param in self.lstm.named_parameters(): + if 'weight_ih' in name: + nn.init.xavier_uniform_(param) + elif 'weight_hh' in name: + nn.init.orthogonal_(param) + elif 'bias' in name: + nn.init.constant_(param, 0) + # Set forget gate bias to 1 + n = param.size(0) + with torch.no_grad(): + param.data[n//4:n//2] = 1.0 + + def forward(self, sequences: torch.Tensor) -> torch.Tensor: + """ + Encode sequences of agent observations. + + Parameters + ---------- + sequences : torch.Tensor + Agent observation sequences [batch_size, seq_len, input_size] + + Returns + ------- + encoding : torch.Tensor + Final hidden state [batch_size, hidden_size] + """ + batch_size = sequences.size(0) + h0 = torch.zeros(self.num_layers, batch_size, self.hidden_size, + device=sequences.device) + c0 = torch.zeros(self.num_layers, batch_size, self.hidden_size, + device=sequences.device) + + _, (hn, _) = self.lstm(sequences, (h0, c0)) + return hn[-1] # Return last layer's hidden state + + +class SquashedGaussianActor(nn.Module): + """ + Stochastic actor network for SAC with squashed Gaussian policy. + + Outputs mean and log_std for a Gaussian distribution, then applies + tanh squashing to bound actions. Uses reparameterization trick for + gradient backpropagation. + + Reference: SAC paper Section 4.2 + """ + + def __init__(self, state_dim: int, action_dim: int, + hidden_dims: List[int] = [256, 256], + max_action: float = 65.0, min_action: float = 0.0, + log_std_min: float = -20.0, log_std_max: float = 2.0): + super(SquashedGaussianActor, self).__init__() + + self.max_action = max_action + self.min_action = min_action + self.action_scale = (max_action - min_action) / 2.0 + self.action_bias = (max_action + min_action) / 2.0 + self.log_std_min = log_std_min + self.log_std_max = log_std_max + + # Build feature network + layers = [] + in_dim = state_dim + for h_dim in hidden_dims: + layers.append(nn.Linear(in_dim, h_dim)) + layers.append(nn.LayerNorm(h_dim)) + layers.append(nn.ReLU()) + in_dim = h_dim + + self.feature_net = nn.Sequential(*layers) + + # Separate heads for mean and log_std + self.mean_head = nn.Linear(hidden_dims[-1], action_dim) + self.log_std_head = nn.Linear(hidden_dims[-1], action_dim) + + self._initialize_weights() + + def _initialize_weights(self): + """Initialize weights for stable training.""" + for layer in self.feature_net: + if isinstance(layer, nn.Linear): + nn.init.orthogonal_(layer.weight, gain=np.sqrt(2)) + nn.init.constant_(layer.bias, 0.0) + + # Small initialization for output heads + nn.init.orthogonal_(self.mean_head.weight, gain=0.01) + nn.init.constant_(self.mean_head.bias, 0.0) + nn.init.orthogonal_(self.log_std_head.weight, gain=0.01) + nn.init.constant_(self.log_std_head.bias, 0.0) + + def forward(self, state: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Forward pass to get mean and log_std. + + Parameters + ---------- + state : torch.Tensor + Input state [batch_size, state_dim] + + Returns + ------- + mean : torch.Tensor + Mean of Gaussian [batch_size, action_dim] + log_std : torch.Tensor + Log standard deviation [batch_size, action_dim] + """ + features = self.feature_net(state) + mean = self.mean_head(features) + log_std = self.log_std_head(features) + # Clamp log_std for numerical stability + log_std = torch.clamp(log_std, self.log_std_min, self.log_std_max) + return mean, log_std + + def sample(self, state: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: + """ + Sample action using reparameterization trick with tanh squashing. + + Parameters + ---------- + state : torch.Tensor + Input state + + Returns + ------- + action : torch.Tensor + Squashed action in [min_action, max_action] + log_prob : torch.Tensor + Log probability of action (corrected for tanh) + mean : torch.Tensor + Mean action (deterministic) + """ + mean, log_std = self.forward(state) + std = log_std.exp() + + # Sample from Gaussian using reparameterization trick + normal = Normal(mean, std) + x_t = normal.rsample() # Reparameterized sample + + # Apply tanh squashing + y_t = torch.tanh(x_t) + + # Scale to action bounds: [-1, 1] -> [min_action, max_action] + action = y_t * self.action_scale + self.action_bias + + # Compute log probability with tanh correction + # log_prob = log_pi(a|s) = log_pi(u|s) - sum(log(1 - tanh(u)^2)) + # where u is pre-tanh action + log_prob = normal.log_prob(x_t) + # Tanh correction (numerically stable version) + log_prob -= torch.log(self.action_scale * (1 - y_t.pow(2)) + 1e-6) + log_prob = log_prob.sum(dim=-1, keepdim=True) + + # Mean action (deterministic) + mean_action = torch.tanh(mean) * self.action_scale + self.action_bias + + return action, log_prob, mean_action + + def get_action(self, state: torch.Tensor, deterministic: bool = False + ) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Get action for inference. + + Parameters + ---------- + state : torch.Tensor + Input state + deterministic : bool + If True, return mean action + + Returns + ------- + action : torch.Tensor + Action value + log_prob : torch.Tensor + Log probability + """ + action, log_prob, mean_action = self.sample(state) + if deterministic: + return mean_action, log_prob + return action, log_prob + + +class TwinQNetwork(nn.Module): + """ + Twin Q-networks for SAC (reduces overestimation bias). + + Takes state and action as input, outputs Q-values from both networks. + """ + + def __init__(self, state_dim: int, action_dim: int, + hidden_dims: List[int] = [256, 256]): + super(TwinQNetwork, self).__init__() + + input_dim = state_dim + action_dim + + # Q1 network + q1_layers = [] + in_dim = input_dim + for h_dim in hidden_dims: + q1_layers.append(nn.Linear(in_dim, h_dim)) + q1_layers.append(nn.ReLU()) + in_dim = h_dim + q1_layers.append(nn.Linear(hidden_dims[-1], 1)) + self.q1 = nn.Sequential(*q1_layers) + + # Q2 network + q2_layers = [] + in_dim = input_dim + for h_dim in hidden_dims: + q2_layers.append(nn.Linear(in_dim, h_dim)) + q2_layers.append(nn.ReLU()) + in_dim = h_dim + q2_layers.append(nn.Linear(hidden_dims[-1], 1)) + self.q2 = nn.Sequential(*q2_layers) + + self._initialize_weights() + + def _initialize_weights(self): + """Initialize Q-network weights.""" + for network in [self.q1, self.q2]: + for layer in network: + if isinstance(layer, nn.Linear): + nn.init.xavier_uniform_(layer.weight) + nn.init.constant_(layer.bias, 0) + + def forward(self, state: torch.Tensor, action: torch.Tensor + ) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Forward pass through both Q-networks. + + Parameters + ---------- + state : torch.Tensor + State input [batch_size, state_dim] + action : torch.Tensor + Action input [batch_size, action_dim] + + Returns + ------- + q1 : torch.Tensor + Q-value from first network + q2 : torch.Tensor + Q-value from second network + """ + x = torch.cat([state, action], dim=-1) + return self.q1(x), self.q2(x) + + def Q1(self, state: torch.Tensor, action: torch.Tensor) -> torch.Tensor: + """Get Q-value from first network only.""" + x = torch.cat([state, action], dim=-1) + return self.q1(x) + + +class SACAlgorithm(BaseAlgorithm): + """ + Soft Actor-Critic (SAC) algorithm with auto-tuning entropy. + + SAC maximizes both expected return and entropy, encouraging exploration + while learning optimal policies. Key features: + - Maximum entropy framework + - Twin Q-networks (reduces overestimation) + - Auto-tuning temperature (alpha) + - Off-policy learning with replay buffer + + The objective is: J(pi) = E[sum(r + alpha * H(pi(.|s)))] + where H is the entropy and alpha is automatically tuned. + + Reference + --------- + Haarnoja et al., "Soft Actor-Critic: Off-Policy Maximum Entropy Deep + Reinforcement Learning with a Stochastic Actor", ICML 2018 + + Parameters + ---------- + config : Dict[str, Any] + SAC configuration + state_dim : int + Individual agent observation dimension + action_dim : int + Action dimension (1 for continuous speed control) + """ + + def __init__(self, config: Dict[str, Any], state_dim: int, action_dim: int): + super().__init__(config, state_dim, action_dim) + + # Device configuration + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + + # =========== SAC Hyperparameters =========== + self.tau = config.get('tau', 0.005) # Soft update coefficient + self.gamma = config.get('gamma', 0.99) # Discount factor + + # Action bounds + self.max_action = config.get('max_action', 65.0) + self.min_action = config.get('min_action', 0.0) + + # Learning rates + self.actor_lr = config.get('learning_rate_actor', 3e-4) + self.critic_lr = config.get('learning_rate_critic', 3e-4) + self.alpha_lr = config.get('learning_rate_alpha', 3e-4) + + # Entropy tuning + self.auto_entropy_tuning = config.get('auto_entropy_tuning', True) + self.target_entropy = config.get('target_entropy', -float(action_dim)) + init_alpha = config.get('init_alpha', 0.2) + + # Warmup and buffer + self.warmup_steps = config.get('warmup_steps', 1000) + self.memory_size = config.get('memory_size', 25000) + self.batch_size = config.get('batch_size', 256) + + # Network architecture + self.lstm_hidden_size = config.get('lstm_hidden_size', 128) + self.lstm_num_layers = config.get('lstm_num_layers', 1) + actor_hidden_dims = config.get('actor_hidden_dims', [256, 256]) + critic_hidden_dims = config.get('critic_hidden_dims', [256, 256]) + + # Log std bounds for actor + log_std_min = config.get('log_std_min', -20) + log_std_max = config.get('log_std_max', 2) + + # =========== Networks =========== + # LSTM encoder for multi-agent context + self.lstm_encoder = LSTMEncoder( + input_size=state_dim, + hidden_size=self.lstm_hidden_size, + num_layers=self.lstm_num_layers + ).to(self.device) + + # Combined state dim for actor/critic: ego_state + lstm_context + combined_state_dim = state_dim + self.lstm_hidden_size + + # Actor (squashed Gaussian policy) + self.actor = SquashedGaussianActor( + state_dim=combined_state_dim, + action_dim=action_dim, + hidden_dims=actor_hidden_dims, + max_action=self.max_action, + min_action=self.min_action, + log_std_min=log_std_min, + log_std_max=log_std_max + ).to(self.device) + + # Twin Q-networks + self.critic = TwinQNetwork( + state_dim=combined_state_dim, + action_dim=action_dim, + hidden_dims=critic_hidden_dims + ).to(self.device) + + # Target Q-networks + self.critic_target = TwinQNetwork( + state_dim=combined_state_dim, + action_dim=action_dim, + hidden_dims=critic_hidden_dims + ).to(self.device) + self.critic_target.load_state_dict(self.critic.state_dict()) + + # =========== Optimizers =========== + self.actor_optimizer = optim.Adam(self.actor.parameters(), lr=self.actor_lr) + self.critic_optimizer = optim.Adam(self.critic.parameters(), lr=self.critic_lr) + + # Auto-tuning alpha (temperature) + if self.auto_entropy_tuning: + self.log_alpha = torch.tensor(np.log(init_alpha), requires_grad=True, + device=self.device, dtype=torch.float32) + self.alpha_optimizer = optim.Adam([self.log_alpha], lr=self.alpha_lr) + self.alpha = self.log_alpha.exp().item() + else: + self.alpha = init_alpha + + # =========== Replay Buffer =========== + self.memory = SmartReplayBuffer(capacity=self.memory_size, recency_ratio=0.5) + + # =========== Training State =========== + self.training = True + self.training_step = 0 + self._pretrained = False + + # Training metrics + self.training_metrics = { + 'actor_loss': 0.0, + 'critic_loss': 0.0, + 'alpha_loss': 0.0, + 'alpha': self.alpha, + 'q1_mean': 0.0, + 'q2_mean': 0.0, + 'entropy': 0.0, + 'memory_size': 0, + } + + logger.info(f"SAC initialized: state_dim={state_dim}, action_dim={action_dim}, " + f"device={self.device}") + logger.info(f"SAC hyperparameters: tau={self.tau}, gamma={self.gamma}, " + f"auto_entropy={self.auto_entropy_tuning}, target_entropy={self.target_entropy}") + + def select_action(self, multi_agent_obs: Dict[str, np.ndarray], + ego_agent_id: str, training: bool = True) -> Optional[float]: + """ + Select action for ego agent using SAC policy. + + Parameters + ---------- + multi_agent_obs : Dict[str, np.ndarray] + All agents' observations + ego_agent_id : str + ID of the ego agent + training : bool + Whether in training mode + + Returns + ------- + action : float or None + Target speed in km/h, or None during warmup + """ + try: + # Warmup phase: random exploration + if training and len(self.memory) < self.warmup_steps and not self._pretrained: + if len(self.memory) % 500 == 0: + logger.debug(f"SAC Warmup: {len(self.memory)}/{self.warmup_steps}") + return np.random.uniform(self.min_action, self.max_action) + + # Prepare inputs + ego_state, multi_agent_context = self._prepare_inputs( + multi_agent_obs, ego_agent_id + ) + + # Combine ego state and multi-agent context + combined_state = torch.cat([ego_state, multi_agent_context], dim=1) + + with torch.no_grad(): + if training: + action, log_prob, _ = self.actor.sample(combined_state) + else: + _, _, action = self.actor.sample(combined_state) # Deterministic (mean) + + final_speed = action.squeeze().item() + final_speed = np.clip(final_speed, self.min_action, self.max_action) + + # Debug logging (periodic) + if len(self.memory) % 2000 == 0: + logger.debug(f"SAC Action: speed={final_speed:.1f}, alpha={self.alpha:.4f}") + + return final_speed + + except Exception as e: + logger.error(f"Error in SAC action selection: {e}") + import traceback + traceback.print_exc() + return None + + def store_transition(self, multi_agent_obs: Dict[str, np.ndarray], + ego_agent_id: str, action: float, reward: float, + next_multi_agent_obs: Dict[str, np.ndarray], done: bool): + """ + Store transition in replay buffer. + + Parameters + ---------- + multi_agent_obs : Dict[str, np.ndarray] + Current observations + ego_agent_id : str + Ego agent ID + action : float + Action taken (speed) + reward : float + Reward received + next_multi_agent_obs : Dict[str, np.ndarray] + Next observations + done : bool + Episode termination flag + """ + try: + # Prepare current state + ego_state, multi_agent_context = self._prepare_inputs( + multi_agent_obs, ego_agent_id + ) + + # Prepare next state + next_ego_state, next_multi_agent_context = self._prepare_inputs( + next_multi_agent_obs, ego_agent_id + ) + + # Store transition + self.memory.push( + ego_state.detach().cpu().numpy(), + multi_agent_context.detach().cpu().numpy(), + action, + reward, + next_ego_state.detach().cpu().numpy(), + next_multi_agent_context.detach().cpu().numpy(), + done + ) + + # Log buffer stats periodically + if len(self.memory) % 5000 == 0: + logger.debug(f"SAC Buffer: {len(self.memory)}/{self.memory_size}") + + except Exception as e: + logger.error(f"Error storing SAC transition: {e}") + + def update(self) -> Dict[str, float]: + """ + Perform SAC update. + + Returns + ------- + metrics : Dict[str, float] + Training metrics from update + """ + # Only update if we have enough samples + if len(self.memory) < self.batch_size: + return self.training_metrics + + try: + # Sample batch + transitions = self.memory.sample(self.batch_size) + + # Unpack batch + ego_states_np = np.stack([t[0] for t in transitions]).squeeze(1).astype(np.float32) + ego_states = torch.from_numpy(ego_states_np).to(self.device) + + multi_contexts_np = np.stack([t[1] for t in transitions]).squeeze(1).astype(np.float32) + multi_agent_contexts = torch.from_numpy(multi_contexts_np).to(self.device) + + actions_np = np.array([t[2] for t in transitions], dtype=np.float32).reshape(-1, 1) + actions = torch.from_numpy(actions_np).to(self.device) + + rewards_np = np.array([t[3] for t in transitions], dtype=np.float32).reshape(-1, 1) + rewards = torch.from_numpy(rewards_np).to(self.device) + + next_ego_np = np.stack([t[4] for t in transitions]).squeeze(1).astype(np.float32) + next_ego_states = torch.from_numpy(next_ego_np).to(self.device) + + next_contexts_np = np.stack([t[5] for t in transitions]).squeeze(1).astype(np.float32) + next_multi_agent_contexts = torch.from_numpy(next_contexts_np).to(self.device) + + dones_np = np.array([t[6] for t in transitions], dtype=np.float32).reshape(-1, 1) + dones = torch.from_numpy(dones_np).to(self.device) + + # Combine states for networks + states = torch.cat([ego_states, multi_agent_contexts], dim=1) + next_states = torch.cat([next_ego_states, next_multi_agent_contexts], dim=1) + + # =========== Update Critics =========== + with torch.no_grad(): + # Sample next actions from current policy + next_actions, next_log_probs, _ = self.actor.sample(next_states) + + # Target Q-values (minimum of two targets for conservatism) + target_q1, target_q2 = self.critic_target(next_states, next_actions) + target_q = torch.min(target_q1, target_q2) + + # Soft Q-target with entropy + target_q = rewards + (1 - dones) * self.gamma * (target_q - self.alpha * next_log_probs) + + # Current Q-values + current_q1, current_q2 = self.critic(states, actions) + + # Critic loss (MSE) + critic_loss = F.mse_loss(current_q1, target_q) + F.mse_loss(current_q2, target_q) + + # Update critics + self.critic_optimizer.zero_grad() + critic_loss.backward() + nn.utils.clip_grad_norm_(self.critic.parameters(), 1.0) + self.critic_optimizer.step() + + # =========== Update Actor =========== + # Sample new actions for actor update + new_actions, log_probs, _ = self.actor.sample(states) + q1_new, q2_new = self.critic(states, new_actions) + q_new = torch.min(q1_new, q2_new) + + # Actor loss: maximize Q - alpha * log_prob + actor_loss = (self.alpha * log_probs - q_new).mean() + + # Update actor + self.actor_optimizer.zero_grad() + actor_loss.backward() + nn.utils.clip_grad_norm_(self.actor.parameters(), 1.0) + self.actor_optimizer.step() + + # =========== Update Alpha (Temperature) =========== + if self.auto_entropy_tuning: + # Alpha loss: minimize -alpha * (log_prob + target_entropy) + alpha_loss = -(self.log_alpha * (log_probs + self.target_entropy).detach()).mean() + + self.alpha_optimizer.zero_grad() + alpha_loss.backward() + self.alpha_optimizer.step() + + self.alpha = self.log_alpha.exp().item() + else: + alpha_loss = torch.tensor(0.0) + + # =========== Soft Update Target Networks =========== + self._soft_update_target() + + # =========== Update Metrics =========== + self.training_step += 1 + self.training_metrics.update({ + 'actor_loss': actor_loss.item(), + 'critic_loss': critic_loss.item(), + 'alpha_loss': alpha_loss.item() if self.auto_entropy_tuning else 0.0, + 'alpha': self.alpha, + 'q1_mean': current_q1.mean().item(), + 'q2_mean': current_q2.mean().item(), + 'entropy': -log_probs.mean().item(), + 'memory_size': len(self.memory), + }) + + # TensorBoard logging + self.log_scalar('Loss/actor', actor_loss.item(), category='losses') + self.log_scalar('Loss/critic', critic_loss.item(), category='losses') + self.log_scalar('SAC/alpha', self.alpha, category='losses') + self.log_scalar('SAC/entropy', -log_probs.mean().item(), category='losses') + self.log_scalar('Q_values/Q1_mean', current_q1.mean().item(), category='q_values') + self.log_scalar('Q_values/Q2_mean', current_q2.mean().item(), category='q_values') + self.log_scalar('Buffer/size', len(self.memory), category='buffer') + + # Log progress periodically + if self.training_step % 200 == 0: + logger.info(f"SAC Step {self.training_step}: " + f"critic_loss={critic_loss.item():.4f}, " + f"actor_loss={actor_loss.item():.4f}, " + f"alpha={self.alpha:.4f}, " + f"entropy={-log_probs.mean().item():.4f}") + + # GPU cleanup + if torch.cuda.is_available() and self.training_step % 100 == 0: + torch.cuda.empty_cache() + gc.collect() + + return self.training_metrics + + except Exception as e: + logger.error(f"Error in SAC update: {e}") + import traceback + traceback.print_exc() + return self.training_metrics + + def _soft_update_target(self): + """Soft update target networks.""" + for param, target_param in zip(self.critic.parameters(), + self.critic_target.parameters()): + target_param.data.copy_( + self.tau * param.data + (1 - self.tau) * target_param.data + ) + + def _prepare_inputs(self, multi_agent_obs: Dict[str, np.ndarray], + ego_agent_id: str) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Prepare inputs for SAC networks. + + Returns + ------- + ego_state : torch.Tensor + Ego agent features [1, state_dim] + multi_agent_context : torch.Tensor + LSTM encoding [1, lstm_hidden_size] + """ + # Get ego observation + if ego_agent_id not in multi_agent_obs: + ego_agent_id = list(multi_agent_obs.keys())[0] + + ego_obs = multi_agent_obs[ego_agent_id] + ego_state = torch.FloatTensor(ego_obs).unsqueeze(0).to(self.device) + + # Prepare agent sequence for LSTM + agent_sequence = [ego_obs] + + # Add other agents sorted by distance + other_agents = [] + for agent_id, obs in multi_agent_obs.items(): + if agent_id != ego_agent_id: + rel_x, rel_y = obs[0], obs[1] + distance = np.sqrt(rel_x*rel_x + rel_y*rel_y) + other_agents.append((obs, distance)) + + other_agents.sort(key=lambda x: x[1]) + for obs, _ in other_agents: + agent_sequence.append(obs) + + # Encode with LSTM + if len(agent_sequence) > 1: + sequence_array = np.array(agent_sequence) + sequence_tensor = torch.FloatTensor(sequence_array).unsqueeze(0).to(self.device) + multi_agent_context = self.lstm_encoder(sequence_tensor) + else: + multi_agent_context = torch.zeros(1, self.lstm_hidden_size).to(self.device) + + return ego_state, multi_agent_context + + def reset_episode(self): + """Reset for new episode.""" + self.episode_count += 1 + + # Log to TensorBoard + if self.writer is not None: + self.writer.add_scalar('SAC/memory_size', len(self.memory), self.episode_count) + self.writer.add_scalar('SAC/alpha', self.alpha, self.episode_count) + + # GPU cleanup every few episodes + if self.episode_count % 5 == 0 and torch.cuda.is_available(): + torch.cuda.empty_cache() + gc.collect() + + def get_training_info(self) -> Dict[str, Any]: + """Get training information.""" + return { + 'algorithm': 'SAC', + 'algorithm_type': 'soft_actor_critic', + 'episode_count': self.episode_count, + 'state_dim': self.state_dim, + 'action_dim': self.action_dim, + 'buffer_size': len(self.memory), + 'training_step': self.training_step, + 'tau': self.tau, + 'gamma': self.gamma, + 'alpha': self.alpha, + 'target_entropy': self.target_entropy, + 'actor_loss': self.training_metrics.get('actor_loss', 0.0), + 'critic_loss': self.training_metrics.get('critic_loss', 0.0), + 'entropy': self.training_metrics.get('entropy', 0.0), + 'epsilon': self.alpha, # For GUI compatibility + 'device': str(self.device), + 'max_action': self.max_action, + 'warmup_steps': self.warmup_steps, + 'in_warmup': len(self.memory) < self.warmup_steps and not self._pretrained, + } + + def log_episode_metrics(self, episode_reward: float, episode_length: int, + success_rate: float = 0.0, collision_rate: float = 0.0, + near_miss_count: int = 0, ttc_violation_rate: float = 0.0, + additional_metrics: Dict[str, float] = None, + traffic_metrics: Dict[str, float] = None): + """Log episode-level metrics to TensorBoard.""" + # Add SAC-specific metrics + sac_metrics = { + 'alpha': self.alpha, + 'training_step': self.training_step, + } + if additional_metrics: + sac_metrics.update(additional_metrics) + + # Call base class method + super().log_episode_metrics( + episode_reward, episode_length, success_rate, collision_rate, + near_miss_count=near_miss_count, + ttc_violation_rate=ttc_violation_rate, + additional_metrics=sac_metrics, + traffic_metrics=traffic_metrics + ) + + def save(self, path: str): + """Save SAC model.""" + try: + save_data = { + 'actor_state_dict': self.actor.state_dict(), + 'critic_state_dict': self.critic.state_dict(), + 'critic_target_state_dict': self.critic_target.state_dict(), + 'lstm_encoder_state_dict': self.lstm_encoder.state_dict(), + 'actor_optimizer_state_dict': self.actor_optimizer.state_dict(), + 'critic_optimizer_state_dict': self.critic_optimizer.state_dict(), + 'episode_count': self.episode_count, + 'training_step': self.training_step, + 'training_metrics': self.training_metrics, + 'alpha': self.alpha, + 'config': self.config, + } + + if self.auto_entropy_tuning: + save_data['log_alpha'] = self.log_alpha.detach().cpu() + save_data['alpha_optimizer_state_dict'] = self.alpha_optimizer.state_dict() + + torch.save(save_data, path) + logger.info(f"SAC model saved to {path}") + except Exception as e: + logger.error(f"Error saving SAC model: {e}") + + def load(self, path: str): + """Load SAC model.""" + try: + save_data = torch.load(path, map_location=self.device, weights_only=False) + + self.actor.load_state_dict(save_data['actor_state_dict']) + self.critic.load_state_dict(save_data['critic_state_dict']) + self.critic_target.load_state_dict(save_data['critic_target_state_dict']) + self.lstm_encoder.load_state_dict(save_data['lstm_encoder_state_dict']) + self.actor_optimizer.load_state_dict(save_data['actor_optimizer_state_dict']) + self.critic_optimizer.load_state_dict(save_data['critic_optimizer_state_dict']) + self.episode_count = save_data.get('episode_count', 0) + self.training_step = save_data.get('training_step', 0) + self.alpha = save_data.get('alpha', 0.2) + + if 'training_metrics' in save_data: + self.training_metrics = save_data['training_metrics'] + + if self.auto_entropy_tuning and 'log_alpha' in save_data: + self.log_alpha = save_data['log_alpha'].to(self.device).requires_grad_(True) + self.alpha_optimizer = optim.Adam([self.log_alpha], lr=self.alpha_lr) + if 'alpha_optimizer_state_dict' in save_data: + self.alpha_optimizer.load_state_dict(save_data['alpha_optimizer_state_dict']) + + self._pretrained = True + logger.info(f"SAC model loaded from {path}") + except Exception as e: + logger.error(f"Error loading SAC model: {e}") diff --git a/opencda_marl/core/marl/algorithms/smart_replay_buffer.py b/opencda_marl/core/marl/algorithms/smart_replay_buffer.py index 9303b67..7b5ac84 100644 --- a/opencda_marl/core/marl/algorithms/smart_replay_buffer.py +++ b/opencda_marl/core/marl/algorithms/smart_replay_buffer.py @@ -1,129 +1,215 @@ """ -Simple Smart Replay Buffer for RL algorithms. -Optimized for small-scale scenarios (~15k transitions). +High-Performance Smart Replay Buffer for RL algorithms. +Uses numpy arrays for O(1) sampling instead of O(N) deque operations. """ -import random import numpy as np -from collections import deque -from typing import List, Tuple, Any +from typing import List, Tuple, Optional from loguru import logger class SmartReplayBuffer: """ - A simple but smart replay buffer with recency bias. - Designed for scenarios with ~15k transitions. + High-performance replay buffer with recency bias. + Uses pre-allocated numpy arrays for O(1) random access and sampling. + + Performance: O(1) push, O(batch_size) sample (instead of O(N) with deque) """ - + def __init__(self, capacity: int = 20000, recency_ratio: float = 0.5): """ Initialize smart replay buffer. - + Args: - capacity: Maximum buffer size (default 20k for your scenario) + capacity: Maximum buffer size recency_ratio: Fraction of samples from recent experiences (0.5 = 50% recent) """ self.capacity = capacity - self.memory = deque(maxlen=capacity) self.recency_ratio = recency_ratio - + + # Circular buffer state + self.position = 0 # Next write position + self.size = 0 # Current number of valid entries + + # Storage arrays - initialized lazily on first push + self._initialized = False + self._num_fields = 0 + self._field_arrays: List[np.ndarray] = [] + self._field_dtypes: List[np.dtype] = [] + self._field_shapes: List[Tuple] = [] + # Track statistics self.total_stored = 0 self.sample_count = 0 - + logger.info(f"SmartReplayBuffer initialized: capacity={capacity}, recency_ratio={recency_ratio}") - + + def _initialize_storage(self, transition: Tuple): + """Initialize numpy arrays based on first transition structure.""" + self._num_fields = len(transition) + + for i, field in enumerate(transition): + if isinstance(field, np.ndarray): + # For numpy arrays, store with original shape + shape = (self.capacity,) + field.shape + dtype = field.dtype + elif isinstance(field, bool): + shape = (self.capacity,) + dtype = np.bool_ + elif isinstance(field, (int, np.integer)): + shape = (self.capacity,) + dtype = np.int64 + else: # float + shape = (self.capacity,) + dtype = np.float32 + + self._field_arrays.append(np.zeros(shape, dtype=dtype)) + self._field_dtypes.append(dtype) + self._field_shapes.append(shape[1:] if len(shape) > 1 else ()) + + self._initialized = True + logger.debug(f"Buffer storage initialized with {self._num_fields} fields") + def push(self, *args): - """Store a transition (compatible with all algorithms).""" - self.memory.append(args) + """Store a transition using circular buffer. O(1) operation.""" + if not self._initialized: + self._initialize_storage(args) + + # Store each field at current position + for i, value in enumerate(args): + if isinstance(value, np.ndarray): + self._field_arrays[i][self.position] = value + else: + self._field_arrays[i][self.position] = value + + # Update circular buffer position + self.position = (self.position + 1) % self.capacity + self.size = min(self.size + 1, self.capacity) self.total_stored += 1 - + def sample(self, batch_size: int) -> List[Tuple]: """ - Sample batch with recency bias. - + Sample batch with recency bias using numpy vectorized operations. + O(batch_size) operation instead of O(N). + 50% from most recent 20% of buffer (recent experiences) 50% from entire buffer (diverse experiences) """ - if len(self.memory) < batch_size: - return list(self.memory) - + if self.size < batch_size: + # Return all available as list of tuples + return self._indices_to_transitions(np.arange(self.size)) + self.sample_count += 1 - + # Calculate split recent_samples = int(batch_size * self.recency_ratio) random_samples = batch_size - recent_samples - - batch = [] - - # Sample from recent experiences (last 20% of buffer) - if recent_samples > 0: - recent_size = max(1, int(len(self.memory) * 0.2)) - recent_portion = list(self.memory)[-recent_size:] - batch.extend(random.sample(recent_portion, - min(recent_samples, len(recent_portion)))) - - # Sample from entire buffer for diversity - if random_samples > 0: - remaining_batch = random.sample(self.memory, random_samples) - batch.extend(remaining_batch) - - # Shuffle to mix recent and random samples - random.shuffle(batch) - + + # Calculate recent window (last 20% of filled buffer) + recent_window_size = max(1, int(self.size * 0.2)) + + # Get valid indices for recent window + # For circular buffer, recent entries are at positions before current position + if self.size < self.capacity: + # Buffer not full yet - recent is simply the last entries + recent_start = max(0, self.size - recent_window_size) + recent_indices = np.random.randint(recent_start, self.size, size=recent_samples) + random_indices = np.random.randint(0, self.size, size=random_samples) + else: + # Buffer is full - handle circular wraparound + # Recent entries are the ones written most recently before position + recent_start = (self.position - recent_window_size) % self.capacity + + if recent_start < self.position: + # No wraparound - recent window is contiguous + recent_indices = np.random.randint(recent_start, self.position, size=recent_samples) + else: + # Wraparound - sample from two segments + segment1_size = self.capacity - recent_start # From recent_start to end + segment2_size = self.position # From 0 to position + total_recent = segment1_size + segment2_size + + # Generate random positions within the conceptual recent window + recent_positions = np.random.randint(0, total_recent, size=recent_samples) + recent_indices = np.where( + recent_positions < segment1_size, + recent_start + recent_positions, # In segment 1 + recent_positions - segment1_size # In segment 2 (wraps to 0) + ) + + random_indices = np.random.randint(0, self.capacity, size=random_samples) + + # Combine and shuffle indices + all_indices = np.concatenate([recent_indices, random_indices]) + np.random.shuffle(all_indices) + # Log statistics occasionally if self.sample_count % 100 == 0: - logger.debug(f"Buffer stats: size={len(self.memory)}/{self.capacity}, " + logger.debug(f"Buffer stats: size={self.size}/{self.capacity}, " f"total_stored={self.total_stored}, samples={self.sample_count}") - + + return self._indices_to_transitions(all_indices) + + def _indices_to_transitions(self, indices: np.ndarray) -> List[Tuple]: + """Convert array indices to list of transition tuples. O(batch_size) operation.""" + # Use numpy fancy indexing - O(batch_size) not O(N) + batch = [] + for i in range(len(indices)): + idx = indices[i] + transition = tuple( + self._field_arrays[f][idx] for f in range(self._num_fields) + ) + batch.append(transition) return batch - + def clear_old(self, keep_ratio: float = 0.7): """ - Clear oldest experiences, keeping only recent ones. - Useful for non-stationary environments. - + Clear oldest experiences by adjusting the virtual buffer window. + O(1) operation - just adjusts pointers, no data copying. + Args: keep_ratio: Fraction of buffer to keep (0.7 = keep 70% newest) """ - if len(self.memory) < 1000: + if self.size < 1000: return # Don't clear if buffer is small - - keep_size = int(len(self.memory) * keep_ratio) - old_size = len(self.memory) - - # Convert to list, slice, convert back - kept_memories = list(self.memory)[-keep_size:] - self.memory = deque(kept_memories, maxlen=self.capacity) - - logger.info(f"Cleared old experiences: {old_size} -> {len(self.memory)}") - + + old_size = self.size + keep_count = int(self.size * keep_ratio) + + # Simply reduce the effective size - oldest entries become invalid + # The circular buffer will overwrite them naturally + self.size = keep_count + + logger.info(f"Cleared old experiences: {old_size} -> {self.size}") + def __len__(self): - return len(self.memory) - + return self.size + def get_stats(self) -> dict: """Get buffer statistics.""" return { - 'current_size': len(self.memory), + 'current_size': self.size, 'capacity': self.capacity, 'total_stored': self.total_stored, 'sample_count': self.sample_count, - 'recency_ratio': self.recency_ratio + 'recency_ratio': self.recency_ratio, + 'position': self.position, + 'initialized': self._initialized } class PrioritizedReplayBuffer: """ - Prioritized Experience Replay buffer using TD-error as priority. + High-performance Prioritized Experience Replay buffer. + Uses numpy arrays for O(1) sampling with cached priority normalization. Based on the PER paper: https://arxiv.org/abs/1511.05952 """ - - def __init__(self, capacity: int = 20000, alpha: float = 0.6, beta: float = 0.4, + + def __init__(self, capacity: int = 20000, alpha: float = 0.6, beta: float = 0.4, beta_increment: float = 0.001): """ Initialize prioritized replay buffer. - + Args: capacity: Maximum buffer size alpha: Priority exponent (0 = uniform, 1 = full prioritization) @@ -131,122 +217,184 @@ def __init__(self, capacity: int = 20000, alpha: float = 0.6, beta: float = 0.4, beta_increment: Beta increment per sampling """ self.capacity = capacity - self.memory = deque(maxlen=capacity) - self.priorities = deque(maxlen=capacity) self.alpha = alpha self.beta = beta self.beta_increment = beta_increment - self.max_priority = 1.0 self.epsilon = 1e-6 # Small constant to ensure non-zero priorities - + + # Circular buffer state + self.position = 0 + self.size = 0 + + # Pre-allocated priority array (always available) + self.priorities = np.zeros(capacity, dtype=np.float32) + self.max_priority = 1.0 + + # Cached probability array (updated lazily) + self._probabilities: Optional[np.ndarray] = None + self._prob_dirty = True # Flag to indicate if probabilities need recalc + + # Storage arrays - initialized lazily on first push + self._initialized = False + self._num_fields = 0 + self._field_arrays: List[np.ndarray] = [] + # Statistics self.total_stored = 0 self.sample_count = 0 - + logger.info(f"PrioritizedReplayBuffer initialized: capacity={capacity}, " f"alpha={alpha}, beta={beta}") - + + def _initialize_storage(self, transition: Tuple): + """Initialize numpy arrays based on first transition structure.""" + self._num_fields = len(transition) + + for i, field in enumerate(transition): + if isinstance(field, np.ndarray): + shape = (self.capacity,) + field.shape + dtype = field.dtype + elif isinstance(field, bool): + shape = (self.capacity,) + dtype = np.bool_ + elif isinstance(field, (int, np.integer)): + shape = (self.capacity,) + dtype = np.int64 + else: + shape = (self.capacity,) + dtype = np.float32 + + self._field_arrays.append(np.zeros(shape, dtype=dtype)) + + self._initialized = True + logger.debug(f"PER storage initialized with {self._num_fields} fields") + def push(self, *args, td_error: float = None): - """Store transition with priority based on TD-error.""" - self.memory.append(args) - - # Use max priority for new transitions (optimistic initialization) - # This ensures new experiences get sampled at least once + """Store transition with priority. O(1) operation.""" + if not self._initialized: + self._initialize_storage(args) + + # Store each field + for i, value in enumerate(args): + self._field_arrays[i][self.position] = value + + # Set priority if td_error is None: priority = self.max_priority else: - # Convert TD-error to priority: |TD-error| + epsilon priority = (abs(td_error) + self.epsilon) ** self.alpha - - self.priorities.append(priority) + + self.priorities[self.position] = priority self.max_priority = max(self.max_priority, priority) + + # Mark probabilities as needing recalculation + self._prob_dirty = True + + # Update circular buffer + self.position = (self.position + 1) % self.capacity + self.size = min(self.size + 1, self.capacity) self.total_stored += 1 - + + def _update_probabilities(self): + """Update cached probability array. Only called when dirty.""" + if not self._prob_dirty: + return + + # Only compute over valid entries + valid_priorities = self.priorities[:self.size] + total = valid_priorities.sum() + if total > 0: + self._probabilities = valid_priorities / total + else: + self._probabilities = np.ones(self.size) / self.size + + self._prob_dirty = False + def sample(self, batch_size: int) -> Tuple[List, np.ndarray, np.ndarray]: - """Sample based on priorities with importance sampling weights.""" - if len(self.memory) < batch_size: - batch = list(self.memory) - indices = list(range(len(self.memory))) - weights = np.ones(len(self.memory)) + """Sample based on priorities with importance sampling weights. O(batch_size) operation.""" + if self.size < batch_size: + indices = np.arange(self.size) + batch = self._indices_to_transitions(indices) + weights = np.ones(self.size, dtype=np.float32) return batch, indices, weights - - # Increment beta for importance sampling + + # Increment beta self.beta = min(1.0, self.beta + self.beta_increment) self.sample_count += 1 - - # Convert priorities to probabilities - priorities = np.array(list(self.priorities)) - probabilities = priorities / priorities.sum() - - # Sample indices based on priorities - indices = np.random.choice(len(self.memory), batch_size, p=probabilities) - + + # Update probabilities if needed (cached) + self._update_probabilities() + + # Sample indices based on priorities - O(batch_size) + indices = np.random.choice(self.size, batch_size, p=self._probabilities, replace=False) + # Calculate importance sampling weights - # w_i = (1 / (N * P(i))) ^ beta - weights = (len(self.memory) * probabilities[indices]) ** (-self.beta) - weights = weights / weights.max() # Normalize weights - + weights = (self.size * self._probabilities[indices]) ** (-self.beta) + weights = weights / weights.max() # Normalize + # Get transitions - batch = [self.memory[idx] for idx in indices] - + batch = self._indices_to_transitions(indices) + # Log statistics occasionally if self.sample_count % 100 == 0: - avg_priority = priorities.mean() - logger.debug(f"PER stats: size={len(self.memory)}, avg_priority={avg_priority:.4f}, " + avg_priority = self.priorities[:self.size].mean() + logger.debug(f"PER stats: size={self.size}, avg_priority={avg_priority:.4f}, " f"beta={self.beta:.3f}, samples={self.sample_count}") - - return batch, indices, weights - + + return batch, indices, weights.astype(np.float32) + + def _indices_to_transitions(self, indices: np.ndarray) -> List[Tuple]: + """Convert indices to transitions. O(batch_size) operation.""" + batch = [] + for idx in indices: + transition = tuple(self._field_arrays[f][idx] for f in range(self._num_fields)) + batch.append(transition) + return batch + def update_priorities(self, indices: np.ndarray, td_errors: np.ndarray): - """Update priorities based on new TD-errors using vectorized operations.""" + """Update priorities based on new TD-errors. Vectorized O(batch_size) operation.""" # Filter valid indices - valid_mask = (indices >= 0) & (indices < len(self.priorities)) + valid_mask = (indices >= 0) & (indices < self.size) valid_indices = indices[valid_mask] valid_td_errors = td_errors[valid_mask] - + if len(valid_indices) == 0: return - - # Vectorized priority calculation - priorities = (np.abs(valid_td_errors) + self.epsilon) ** self.alpha - - # Update priorities in batch - for idx, priority in zip(valid_indices, priorities): - self.priorities[idx] = priority - + + # Vectorized priority update - no loop + new_priorities = (np.abs(valid_td_errors) + self.epsilon) ** self.alpha + self.priorities[valid_indices] = new_priorities + # Update max priority - self.max_priority = max(self.max_priority, np.max(priorities)) - + self.max_priority = max(self.max_priority, new_priorities.max()) + + # Mark probabilities as needing recalculation + self._prob_dirty = True + def clear_old(self, keep_ratio: float = 0.5): - """Clear oldest experiences while preserving priorities.""" - if len(self.memory) < 5000: + """Clear oldest experiences. O(1) operation - just adjusts size.""" + if self.size < 5000: return - - keep_size = int(len(self.memory) * keep_ratio) - old_size = len(self.memory) - - # Keep newest experiences and their priorities - kept_memories = list(self.memory)[-keep_size:] - kept_priorities = list(self.priorities)[-keep_size:] - - self.memory = deque(kept_memories, maxlen=self.capacity) - self.priorities = deque(kept_priorities, maxlen=self.capacity) - - logger.info(f"PER: Cleared old experiences: {old_size} -> {len(self.memory)}") - + + old_size = self.size + self.size = int(self.size * keep_ratio) + self._prob_dirty = True + + logger.info(f"PER: Cleared old experiences: {old_size} -> {self.size}") + def __len__(self): - return len(self.memory) - + return self.size + def get_stats(self) -> dict: """Get buffer statistics.""" - priorities_array = np.array(list(self.priorities)) if self.priorities else np.array([0]) + valid_priorities = self.priorities[:self.size] if self.size > 0 else np.array([0]) return { - 'current_size': len(self.memory), + 'current_size': self.size, 'capacity': self.capacity, 'total_stored': self.total_stored, 'sample_count': self.sample_count, 'alpha': self.alpha, 'beta': self.beta, - 'avg_priority': priorities_array.mean(), + 'avg_priority': float(valid_priorities.mean()), 'max_priority': self.max_priority } \ No newline at end of file diff --git a/opencda_marl/core/marl/algorithms/td3.py b/opencda_marl/core/marl/algorithms/td3.py index 54c4c26..0ea1cd1 100644 --- a/opencda_marl/core/marl/algorithms/td3.py +++ b/opencda_marl/core/marl/algorithms/td3.py @@ -11,6 +11,7 @@ import torch.optim as optim import numpy as np import random +import gc from collections import deque from typing import Dict, Any, List, Tuple from loguru import logger @@ -79,7 +80,7 @@ class Actor(nn.Module): """Actor network for TD3 based on AdvRAIM architecture""" def __init__(self, state_dim: int, lstm_hidden_dim: int, action_dim: int, - max_action: float = 1.0, min_action: float = 0.0, motion_planner_config: dict = None, + max_action: float = 1.0, min_action: float = 0.0, motion_planner_config: dict = None, is_target: bool = False): super(Actor, self).__init__() @@ -87,6 +88,7 @@ def __init__(self, state_dim: int, lstm_hidden_dim: int, action_dim: int, self.min_action = min_action self.is_target = is_target self.forward_count = 0 # Counter for logging frequency + self.action_dim = action_dim # Store for LayerNorm # Use configurable motion planner architecture if motion_planner_config is None: @@ -112,14 +114,14 @@ def __init__(self, state_dim: int, lstm_hidden_dim: int, action_dim: int, # Config case - properly pair input_dim[i] -> output_dim[i] in_dim = layer_dims[i] out_dim = output_dims[i] - + motion_planner_layers.append(nn.Linear(in_dim, out_dim)) # Add ReLU for all layers except the last one if i < num_layers - 1: motion_planner_layers.append(nn.ReLU()) - + self.motion_planner = nn.Sequential(*motion_planner_layers) - + # Final output layer (if the last layer doesn't output action_dim directly) if output_dim != action_dim: self.output_layer = nn.Linear(output_dim, action_dim) @@ -127,6 +129,10 @@ def __init__(self, state_dim: int, lstm_hidden_dim: int, action_dim: int, # Motion planner already outputs the right dimension self.output_layer = None + # LayerNorm before tanh to prevent gradient vanishing from tanh saturation + # This normalizes pre-tanh values to prevent |x| >> 3 where tanh'(x) ≈ 0 + self.pre_tanh_norm = nn.LayerNorm(action_dim) + # Initialize weights properly (only log for non-target networks) self._initialize_weights() @@ -146,22 +152,29 @@ def forward(self, ego_state, multi_agent_context): # Forward through motion planner x = self.motion_planner(combined_input) - - # Output layer with sigmoid activation (if separate output layer exists) + + # Output layer (if separate output layer exists) if self.output_layer is not None: x = self.output_layer(x) - - # Apply tanh activation for better gradient flow and exploration + + # CRITICAL FIX: Apply LayerNorm before tanh to prevent gradient vanishing + # Problem: When |x| > 3, tanh saturates at ±1 and gradient ≈ 0 + # Solution: LayerNorm keeps pre-tanh values in [-2, 2] range where tanh has healthy gradients + x_normalized = self.pre_tanh_norm(x) + + # Apply tanh activation for bounded output # tanh outputs in [-1, 1], then scale to [min_action, max_action] - tanh_out = torch.tanh(x) + tanh_out = torch.tanh(x_normalized) # Scale from [-1, 1] to [0, 1] then to [min_action, max_action] action = self.min_action + (self.max_action - self.min_action) * (tanh_out + 1) * 0.5 self.forward_count = (self.forward_count + 1) % 100000 - + # Debug logging for network outputs (periodic) if not self.is_target and self.forward_count % 5000 == 0: - logger.info(f"Actor Debug (batch mean) - Pre-tanh: {x.mean().item():.3f}, Tanh: {tanh_out.mean().item():.3f}, Action: {action.mean().item():.1f} km/h") - + logger.debug(f"Actor Debug (batch mean) - Pre-norm: {x.mean().item():.3f}, " + f"Normalized: {x_normalized.mean().item():.3f}, " + f"Tanh: {tanh_out.mean().item():.3f}, Action: {action.mean().item():.1f} km/h") + return action def _initialize_weights(self): @@ -323,7 +336,12 @@ def __init__(self, config: Dict[str, Any], state_dim: int, action_dim: int): self.noise_clip = config.get('noise_clip', 0.3) self.exploration_noise = config.get('exploration_noise', 0.1) self.policy_freq = config.get('policy_freq', 2) - self.max_action = config.get('max_action', 60.0) # Maximum speed in km/h + + # Exploration noise decay parameters + self.initial_exploration_noise = self.exploration_noise # Store initial value + self.noise_decay = config.get('noise_decay', 0.995) # Decay rate per episode + self.min_exploration_noise = config.get('min_exploration_noise', 0.05) # Minimum noise floor + self.max_action = config.get('max_action', 65.0) # Maximum speed in km/h self.min_action = config.get('min_action', 0.0) # Minimum speed in km/h # Warmup configuration @@ -332,9 +350,9 @@ def __init__(self, config: Dict[str, Any], state_dim: int, action_dim: int): # Legacy network architecture (for compatibility) critic_hidden_dims = config.get('critic_hidden_dims', [256, 256]) - # Learning rates - self.actor_lr = config.get('learning_rate_actor', 1e-5) - self.critic_lr = config.get('learning_rate_critic', 1e-4) + # Learning rates (increased defaults for faster learning with LayerNorm fix) + self.actor_lr = config.get('learning_rate_actor', 1e-4) # Was 1e-5, now 1e-4 + self.critic_lr = config.get('learning_rate_critic', 1e-3) # Was 1e-4, now 1e-3 # Device configuration self.device = torch.device( @@ -404,6 +422,8 @@ def __init__(self, config: Dict[str, Any], state_dim: int, action_dim: int): recency_ratio=self.recency_ratio) # Training metrics + # Note: actor_grad_norm values are None when actor hasn't updated yet + # This prevents logging 0 values when actor update is delayed self.training_metrics = { 'actor_loss': 0.0, 'critic_loss': 0.0, @@ -411,14 +431,22 @@ def __init__(self, config: Dict[str, Any], state_dim: int, action_dim: int): 'q2_mean': 0.0, # Delayed policy updates (actor updates every policy_freq steps) 'target_updates': 0, - 'memory_size': 0 + 'memory_size': 0, + 'actor_grad_norm_pre': None, # None until first actor update + 'actor_grad_norm_post': None # None until first actor update } self.training = True self.training_step = 0 # Required for delayed policy updates + self._pretrained = False # Flag to indicate if loaded from pretrained checkpoint + + # TensorBoard logging is handled by BaseAlgorithm + # Config options: tensorboard.enabled, tensorboard.log_dir, tensorboard.metrics logger.info( f"TD3 initialized with {state_dim}D states, LSTM hidden: {self.lstm_hidden_size}, device: {self.device}") + logger.info( + f"TD3 Learning rates: actor_lr={self.actor_lr}, critic_lr={self.critic_lr}") def select_action(self, multi_agent_obs: Dict[str, np.ndarray], ego_agent_id: str, training: bool = True) -> float: """ @@ -434,7 +462,8 @@ def select_action(self, multi_agent_obs: Dict[str, np.ndarray], ego_agent_id: st """ try: # During warmup phase, return None to use vanilla agent - if len(self.memory) < self.warmup_steps: + # Skip warmup if model was loaded from checkpoint (_pretrained=True) + if len(self.memory) < self.warmup_steps and not self._pretrained: # Log warmup progress only every 2000 samples to reduce verbosity if len(self.memory) % 2000 == 0: logger.info(f"TD3: Warmup phase {len(self.memory)}/{self.warmup_steps}, using vanilla agent") @@ -450,10 +479,29 @@ def select_action(self, multi_agent_obs: Dict[str, np.ndarray], ego_agent_id: st # Add exploration noise during training if training: - noise = torch.randn_like( - raw_action) * self.exploration_noise - action = torch.clamp( - raw_action + noise, self.min_action, self.max_action) + # SMART EXPLORATION: Scale noise by distance to intersection + # ego_state shape: [1, state_dim], index 6 = dist_to_intersection + dist_to_intersection = ego_state[0, 6].item() + + # Distance-based noise scaling and speed bias + # Thresholds scaled for ~35m spawn-to-intersection distance + if dist_to_intersection > 25.0: + # FAR (25-35m): Initial approach - encourage faster exploration + noise_scale = 1.3 + speed_bias = 5.0 + elif dist_to_intersection > 10.0: + # MID (10-25m): Decision zone - balanced exploration + noise_scale = 1.0 + speed_bias = 0.0 + else: + # NEAR (<10m): Intersection entry - slight caution + noise_scale = 0.8 + speed_bias = -3.0 + + # Apply scaled noise with position-based bias + noise = torch.randn_like(raw_action) * self.exploration_noise * noise_scale + action = raw_action + noise + speed_bias + action = torch.clamp(action, self.min_action, self.max_action) else: action = raw_action @@ -462,17 +510,12 @@ def select_action(self, multi_agent_obs: Dict[str, np.ndarray], ego_agent_id: st if final_speed > self.max_action: final_speed = self.max_action - + # Debug logging for action selection (periodic) if len(self.memory) % 2000 == 0: raw_val = raw_action.squeeze().item() - logger.info(f"TD3 Action Debug - Raw: {raw_val:.1f}, +Noise: {final_speed:.1f}, Episode: {self.episode_count}") - - # Force exploration in early episodes to break out of 30 km/h trap - if self.episode_count < 3 and training and not self._pretrained: - forced_speed = np.random.uniform(35, 60) # Force higher speeds - logger.debug(f"Forced exploration Episode {self.episode_count}: {forced_speed:.1f} km/h (was {final_speed:.1f})") - return forced_speed + dist_info = f", dist={dist_to_intersection:.0f}m" if training else "" + logger.debug(f"TD3 Action Debug - Raw: {raw_val:.1f}, +Noise: {final_speed:.1f}, Episode: {self.episode_count}{dist_info}") return final_speed @@ -577,23 +620,32 @@ def update(self) -> Dict[str, float]: indices = None importance_weights = None - # Unpack batch - squeeze the stored [1, dim] arrays to [dim] - # Create tensors with gradient tracking for critic learning - ego_states = torch.FloatTensor( - np.stack([t[0].squeeze(0) for t in transitions])).to(self.device).requires_grad_(True) - multi_agent_contexts = torch.FloatTensor( - np.stack([t[1].squeeze(0) for t in transitions])).to(self.device).requires_grad_(True) - # Actions: collect scalar actions and reshape to [batch_size, 1] for critic - actions = torch.FloatTensor( - np.array([t[2] for t in transitions])).unsqueeze(1).to(self.device).requires_grad_(False) - rewards = torch.FloatTensor( - np.array([t[3] for t in transitions])).to(self.device) - next_ego_states = torch.FloatTensor( - np.stack([t[4].squeeze(0) for t in transitions])).to(self.device) - next_multi_agent_contexts = torch.FloatTensor( - np.stack([t[5].squeeze(0) for t in transitions])).to(self.device) - dones = torch.BoolTensor( - np.array([t[6] for t in transitions])).to(self.device) + # Unpack batch - optimized: stack first, then squeeze once (not per-element) + # Use torch.from_numpy for efficiency (avoids data copy) + batch_size = len(transitions) + + # Stack arrays first (shape: [batch, 1, dim]), then squeeze to [batch, dim] + ego_states_np = np.stack([t[0] for t in transitions]).squeeze(1).astype(np.float32) + ego_states = torch.from_numpy(ego_states_np).to(self.device).requires_grad_(True) + + multi_contexts_np = np.stack([t[1] for t in transitions]).squeeze(1).astype(np.float32) + multi_agent_contexts = torch.from_numpy(multi_contexts_np).to(self.device).requires_grad_(True) + + # Actions: collect scalars and reshape to [batch_size, 1] + actions_np = np.array([t[2] for t in transitions], dtype=np.float32).reshape(-1, 1) + actions = torch.from_numpy(actions_np).to(self.device) + + rewards_np = np.array([t[3] for t in transitions], dtype=np.float32) + rewards = torch.from_numpy(rewards_np).to(self.device) + + next_ego_np = np.stack([t[4] for t in transitions]).squeeze(1).astype(np.float32) + next_ego_states = torch.from_numpy(next_ego_np).to(self.device) + + next_contexts_np = np.stack([t[5] for t in transitions]).squeeze(1).astype(np.float32) + next_multi_agent_contexts = torch.from_numpy(next_contexts_np).to(self.device) + + dones_np = np.array([t[6] for t in transitions], dtype=np.bool_) + dones = torch.from_numpy(dones_np).to(self.device) # Debug logging for tensor shapes (only every 1000 updates) if self.training_step % 1000 == 0: @@ -629,6 +681,16 @@ def update(self) -> Dict[str, float]: 'memory_size': len(self.memory) }) + # TensorBoard logging (using base class methods) + self.log_scalar('Loss/critic', critic_loss, category='losses') + if self.training_step % self.policy_freq == 0: + self.log_scalar('Loss/actor', self.training_metrics.get('actor_loss', 0.0), category='losses') + self.log_scalar('Buffer/size', len(self.memory), category='buffer') + # Log Q-values if available + if 'q1_mean' in self.training_metrics: + self.log_scalar('Q_values/Q1_mean', self.training_metrics['q1_mean'], category='q_values') + self.log_scalar('Q_values/Q2_mean', self.training_metrics['q2_mean'], category='q_values') + # Log training progress periodically (every 200 steps) if self.training_step % 200 == 0: current_actor_loss = self.training_metrics.get('actor_loss', 0.0) @@ -637,12 +699,32 @@ def update(self) -> Dict[str, float]: f"TD3 Step {self.training_step}: critic_loss={critic_loss:.4f}, actor_loss={current_actor_loss:.4f}, policy_updates={target_updates}, memory={len(self.memory)}") self.training_step += 1 + + # Explicit cleanup to prevent memory leaks + del ego_states, multi_agent_contexts, actions, rewards + del next_ego_states, next_multi_agent_contexts, dones + del ego_states_np, multi_contexts_np, actions_np, rewards_np + del next_ego_np, next_contexts_np, dones_np + del transitions + if self.use_per: + del importance_weights + + # Periodic deep cleanup to prevent CUDA memory fragmentation + # More aggressive: every 50 steps instead of 500 to prevent slowdown + if self.training_step % 50 == 0 and torch.cuda.is_available(): + torch.cuda.empty_cache() + gc.collect() + return self.training_metrics.copy() except Exception as e: logger.error(f"Error in TD3 update (step {self.training_step}): {e}") import traceback logger.error(f"TD3 update traceback:\n{traceback.format_exc()}") + # Cleanup even on error + if torch.cuda.is_available(): + torch.cuda.empty_cache() + gc.collect() return self.training_metrics.copy() def _prepare_inputs(self, multi_agent_obs: Dict[str, np.ndarray], ego_agent_id: str) -> Tuple[torch.Tensor, torch.Tensor]: @@ -755,7 +837,15 @@ def _update_critic(self, ego_states, multi_agent_contexts, actions, rewards, # Optimize critic self.critic_optimizer.zero_grad() critic_loss.backward() + + # Compute gradient norm BEFORE clipping (for monitoring) + critic_grad_norm_pre = self._compute_grad_norm(self.critic.parameters()) + torch.nn.utils.clip_grad_norm_(self.critic.parameters(), max_norm=1.0) + + # Compute gradient norm AFTER clipping + critic_grad_norm_post = self._compute_grad_norm(self.critic.parameters()) + self.critic_optimizer.step() # Log loss values after backward pass (safe to call .item() now) @@ -763,12 +853,21 @@ def _update_critic(self, ego_states, multi_agent_contexts, actions, rewards, loss_value = critic_loss.item() loss1_value = critic_loss_1.item() loss2_value = critic_loss_2.item() - + + # Store gradient norms in metrics + self.training_metrics['critic_grad_norm_pre'] = critic_grad_norm_pre + self.training_metrics['critic_grad_norm_post'] = critic_grad_norm_post + + # Log to TensorBoard + self.log_scalar('Gradients/critic_pre_clip', critic_grad_norm_pre, category='losses') + self.log_scalar('Gradients/critic_post_clip', critic_grad_norm_post, category='losses') + # Log every 100 updates or when loss is significantly high/low should_log_loss = (self.training_step % 100 == 0) or (loss_value > 5.0) or (loss_value < 0.01) if should_log_loss: - logger.info(f"TD3 Critic Step {self.training_step}: loss1={loss1_value:.4f}, loss2={loss2_value:.4f}, total={loss_value:.4f}") - + logger.info(f"TD3 Critic Step {self.training_step}: loss1={loss1_value:.4f}, loss2={loss2_value:.4f}, " + f"total={loss_value:.4f}, grad_norm={critic_grad_norm_pre:.4f}->{critic_grad_norm_post:.4f}") + return loss_value def _update_critic_with_per(self, ego_states, multi_agent_contexts, actions, rewards, @@ -818,16 +917,24 @@ def _update_critic_with_per(self, ego_states, multi_agent_contexts, actions, rew # Optimize critic self.critic_optimizer.zero_grad() total_loss.backward() + + # Compute gradient norm BEFORE clipping (for monitoring) + critic_grad_norm_pre = self._compute_grad_norm(self.critic.parameters()) + torch.nn.utils.clip_grad_norm_(self.critic.parameters(), max_norm=1.0) + + # Compute gradient norm AFTER clipping + critic_grad_norm_post = self._compute_grad_norm(self.critic.parameters()) + self.critic_optimizer.step() - + # Store loss values for metrics loss_value = total_loss.item() - + with torch.no_grad(): loss1_value = weighted_loss1.item() loss2_value = weighted_loss2.item() - + # Update training metrics self.training_metrics.update({ 'critic_loss1': loss1_value, @@ -836,15 +943,21 @@ def _update_critic_with_per(self, ego_states, multi_agent_contexts, actions, rew 'q2_mean': current_q2.mean().item(), 'target_q_mean': target_q_values.mean().item(), 'td_error_mean': td_errors.mean().item(), - 'importance_weights_mean': importance_weights.mean().item() + 'importance_weights_mean': importance_weights.mean().item(), + 'critic_grad_norm_pre': critic_grad_norm_pre, + 'critic_grad_norm_post': critic_grad_norm_post }) - + + # Log gradient norms to TensorBoard + self.log_scalar('Gradients/critic_pre_clip', critic_grad_norm_pre, category='losses') + self.log_scalar('Gradients/critic_post_clip', critic_grad_norm_post, category='losses') + # Log every 100 updates or when loss is significantly high/low should_log_loss = (self.training_step % 100 == 0) or (loss_value > 5.0) or (loss_value < 0.01) if should_log_loss: - logger.info(f"TD3 PER Step {self.training_step}: loss1={loss1_value:.4f}, loss2={loss2_value:.4f}, " - f"td_error_avg={td_errors.mean().item():.4f}, weights_avg={importance_weights.mean().item():.3f}") - + logger.debug(f"TD3 PER Step {self.training_step}: loss1={loss1_value:.4f}, loss2={loss2_value:.4f}, " + f"td_error_avg={td_errors.mean().item():.4f}, grad_norm={critic_grad_norm_pre:.4f}->{critic_grad_norm_post:.4f}") + return loss_value, td_errors def _update_actor(self, ego_states, multi_agent_contexts): @@ -861,13 +974,31 @@ def _update_actor(self, ego_states, multi_agent_contexts): # Optimize actor self.actor_optimizer.zero_grad() actor_loss.backward() + + # Compute gradient norm BEFORE clipping (for monitoring) + actor_grad_norm_pre = self._compute_grad_norm(self.actor.parameters()) + torch.nn.utils.clip_grad_norm_(self.actor.parameters(), max_norm=1.0) + + # Compute gradient norm AFTER clipping + actor_grad_norm_post = self._compute_grad_norm(self.actor.parameters()) + self.actor_optimizer.step() # Unfreeze critic parameters for param in self.critic.parameters(): param.requires_grad = True + # Store gradient norms in metrics (only when actor actually updates) + self.training_metrics['actor_grad_norm_pre'] = actor_grad_norm_pre + self.training_metrics['actor_grad_norm_post'] = actor_grad_norm_post + + # Log gradient norms to TensorBoard only when actor updates + # This prevents logging 0 values during delayed policy updates + if actor_grad_norm_pre > 0: # Only log when there are actual gradients + self.log_scalar('Gradients/actor_pre_clip', actor_grad_norm_pre, category='losses') + self.log_scalar('Gradients/actor_post_clip', actor_grad_norm_post, category='losses') + return actor_loss.item() def _update_target_networks(self): @@ -882,10 +1013,45 @@ def _update_target_networks(self): target_param.data.copy_( self.tau * param.data + (1 - self.tau) * target_param.data) + def _compute_grad_norm(self, parameters) -> float: + """ + Compute the L2 norm of gradients for monitoring training stability. + Optimized: Single GPU-CPU sync instead of per-parameter sync. + + Args: + parameters: Iterator of model parameters + + Returns: + Total gradient norm (float) + """ + # Collect all gradients - avoid multiple .item() calls + grads = [p.grad.flatten() for p in parameters if p.grad is not None] + if not grads: + return 0.0 + # Single concatenation and norm computation on GPU, then one .item() + return torch.cat(grads).norm(2).item() + def reset_episode(self): """Reset for new episode""" self.episode_count += 1 - + + # Decay exploration noise each episode + old_noise = self.exploration_noise + self.exploration_noise = max( + self.min_exploration_noise, + self.exploration_noise * self.noise_decay + ) + + # Log noise decay to TensorBoard + if self.writer is not None: + self.writer.add_scalar('TD3/exploration_noise', self.exploration_noise, self.episode_count) + self.writer.add_scalar('TD3/noise_decay_ratio', self.exploration_noise / self.initial_exploration_noise, self.episode_count) + + # Log noise decay periodically + if self.episode_count % 10 == 0: + logger.info(f"Episode {self.episode_count}: Exploration noise decayed {old_noise:.4f} → {self.exploration_noise:.4f} " + f"(min: {self.min_exploration_noise}, decay: {self.noise_decay})") + # Auto-clear buffer every N episodes to prevent stale experiences if self.clear_episodes and self.episode_count > 0: if self.episode_count % self.clear_episodes == 0: @@ -896,6 +1062,22 @@ def reset_episode(self): logger.info(f"Episode {self.episode_count}: Cleared old experiences: " f"{old_size} → {new_size} (kept {self.clear_keep_ratio*100:.0f}% newest)") + # GPU memory cleanup to prevent slowdown over episodes + if torch.cuda.is_available(): + # Clear CUDA cache every episode to prevent memory fragmentation + torch.cuda.empty_cache() + + # Log GPU memory usage periodically for debugging + if self.episode_count % 5 == 0: + allocated = torch.cuda.memory_allocated(self.device) / 1024**2 # MB + cached = torch.cuda.memory_reserved(self.device) / 1024**2 # MB + logger.debug(f"Episode {self.episode_count} GPU memory: " + f"allocated={allocated:.1f}MB, cached={cached:.1f}MB") + + # Force garbage collection every few episodes to prevent memory leaks + if self.episode_count % 3 == 0: + gc.collect() + def get_training_info(self) -> Dict[str, Any]: """Get training information""" return { @@ -911,14 +1093,37 @@ def get_training_info(self) -> Dict[str, Any]: 'clear_episodes': self.clear_episodes, 'clear_keep_ratio': self.clear_keep_ratio, 'target_updates': self.training_metrics.get('target_updates', 0), - # TD3 uses exploration noise instead of epsilon - 'epsilon': self.exploration_noise, + # TD3 exploration noise parameters + 'epsilon': self.exploration_noise, # Current noise level (for GUI compatibility) + 'exploration_noise': self.exploration_noise, + 'initial_exploration_noise': self.initial_exploration_noise, + 'min_exploration_noise': self.min_exploration_noise, + 'noise_decay': self.noise_decay, 'actor_loss': self.training_metrics.get('actor_loss', 0.0), 'critic_loss': self.training_metrics.get('critic_loss', 0.0), 'device': str(self.device), 'max_action': self.max_action } + def log_episode_metrics(self, episode_reward: float, episode_length: int, + success_rate: float = 0.0, collision_rate: float = 0.0, + near_miss_count: int = 0, + ttc_violation_rate: float = 0.0, + additional_metrics: Dict[str, float] = None, + traffic_metrics: Dict[str, float] = None): + """Log episode-level metrics to TensorBoard (extends base class)""" + # Add TD3-specific metrics + td3_metrics = {'buffer_size': len(self.memory)} + if additional_metrics: + td3_metrics.update(additional_metrics) + # Call base class method + super().log_episode_metrics( + episode_reward, episode_length, success_rate, collision_rate, + near_miss_count=near_miss_count, + ttc_violation_rate=ttc_violation_rate, + additional_metrics=td3_metrics, traffic_metrics=traffic_metrics + ) + def save(self, path: str): """Save TD3 model""" try: @@ -963,7 +1168,9 @@ def load(self, path: str): if 'training_metrics' in save_data: self.training_metrics = save_data['training_metrics'] - logger.info(f"TD3 model loaded from {path}") + # Mark as pretrained to skip warmup phase + self._pretrained = True + logger.info(f"TD3 model loaded from {path} (skipping warmup)") except Exception as e: logger.error(f"Error loading TD3 model: {e}") diff --git a/opencda_marl/core/marl/extractor.py b/opencda_marl/core/marl/extractor.py index ac1e1b5..d55dc45 100644 --- a/opencda_marl/core/marl/extractor.py +++ b/opencda_marl/core/marl/extractor.py @@ -19,6 +19,30 @@ def __init__(self, config: Dict, algorithm: str): self.features_config = self.td3_config.get('features', {}) self.custom_features = self.features_config # Direct access to features self.state_dim = self._calculate_custom_feature_dim() + elif algorithm == 'mappo': + # MAPPO uses same feature config as TD3 for fair comparison + self.mappo_config = config.get('mappo', {}) + # Try to get features from mappo config, fallback to td3 config + self.features_config = self.mappo_config.get('features', + config.get('td3', {}).get('features', {})) + self.custom_features = self.features_config + self.state_dim = self._calculate_custom_feature_dim() + elif algorithm == 'sac': + # SAC uses same feature extraction as TD3/MAPPO + self.sac_config = config.get('sac', {}) + self.features_config = self.sac_config.get('features', {}) + self.custom_features = self.features_config + self.state_dim = self._calculate_custom_feature_dim() + elif algorithm == 'dqn': + # DQN can use configurable features (8D) or fallback to legacy 7D + self.dqn_config = config.get('dqn', {}) + self.features_config = self.dqn_config.get('features', {}) + if self.features_config: + self.custom_features = self.features_config + self.state_dim = self._calculate_custom_feature_dim() + else: + # Fallback to legacy 7D continuous features + self.state_dim = 7 def extract(self, observations: Dict) -> Dict[int, np.ndarray]: """ @@ -32,8 +56,15 @@ def extract(self, observations: Dict) -> Dict[int, np.ndarray]: """ if self.algorithm == 'q_learning': return self._extract_discrete(observations) - elif self.algorithm == 'td3': + elif self.algorithm in ('td3', 'mappo', 'sac'): + # TD3, MAPPO, and SAC use multi-agent observations return self._extract_multi_agent(observations) + elif self.algorithm == 'dqn': + # DQN uses custom features but returns simple state arrays (not multi-agent format) + if hasattr(self, 'custom_features') and self.custom_features: + return self._extract_custom_features(observations) + else: + return self._extract_continuous(observations) else: return self._extract_continuous(observations) @@ -182,6 +213,12 @@ def _extract_custom_features(self, observations: Dict) -> Dict[int, np.ndarray]: # Default to middle lane (index 2) state_features.extend([0.0, 0.0, 1.0, 0.0]) + elif feature_name == 'speed': + if 'speed' in obs: + state_features.append(obs['speed']) + else: + state_features.append(0.0) + elif feature_name == 'heading_angle': if 'heading_angle' in obs: state_features.append(obs['heading_angle']) @@ -211,7 +248,19 @@ def _extract_custom_features(self, observations: Dict) -> Dict[int, np.ndarray]: # Rough estimate: ~2m per waypoint estimated_waypoints = min(50.0, max(0.0, dist_to_int / 2.0)) state_features.append(estimated_waypoints) - + + elif feature_name == 'nearby_vehicles': + # Nearby vehicle features: 35D = 5 vehicles × 7 features each + # Features per vehicle: rel_x, rel_y, rel_vx, rel_vy, heading_diff, distance, ttc + if 'nearby_vehicles' in obs: + nearby_features = obs['nearby_vehicles'] + state_features.extend(nearby_features) + else: + # Default: 5 slots × 7 features with safe values + # Empty slots have zeros except distance=1.0, ttc=1.0 (safe/far away) + for _ in range(5): + state_features.extend([0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0]) + else: logger.warning(f"Unknown custom feature: {feature_name}") state_features.extend([0.0] * feature_dim) diff --git a/opencda_marl/core/marl/marl_manager.py b/opencda_marl/core/marl/marl_manager.py index 949eafd..5b8b42c 100644 --- a/opencda_marl/core/marl/marl_manager.py +++ b/opencda_marl/core/marl/marl_manager.py @@ -9,7 +9,7 @@ from loguru import logger from .extractor import ObservationExtractor -from .algorithms import QLearningAlgorithm, DQNAlgorithm, TD3Algorithm +from .algorithms import QLearningAlgorithm, DQNAlgorithm, TD3Algorithm, MAPPOAlgorithm, SACAlgorithm class MARLManager: @@ -26,12 +26,41 @@ def __init__(self, config: Dict, algorithm: str): logger.success(f"MARLManager initialized with {algorithm}") + def _merge_shared_config(self, algo_config: Dict) -> Dict: + """Merge shared config (tensorboard, training mode, etc.) with algorithm-specific config.""" + merged = algo_config.copy() + + # Pass training_mode to algorithm (affects TensorBoard and learning) + training_config = self.config.get('training', {}) + merged['training_mode'] = training_config.get('training_mode', True) + + # Merge tensorboard config (algorithm can override) + tb_config = self.config.get('tensorboard', {}) + if 'tensorboard' not in merged: + merged['tensorboard'] = tb_config + elif isinstance(merged['tensorboard'], dict): + # Algorithm-specific overrides shared config + base_tb = tb_config.copy() if isinstance(tb_config, dict) else {} + base_tb.update(merged['tensorboard']) + merged['tensorboard'] = base_tb + + # Disable TensorBoard in evaluation mode (training_mode: false) + # This prevents recording non-training metrics during checkpoint testing + if not merged['training_mode']: + if isinstance(merged.get('tensorboard'), dict): + merged['tensorboard']['enabled'] = False + else: + merged['tensorboard'] = {'enabled': False} + logger.info("Evaluation mode: TensorBoard disabled (training_mode: false)") + + return merged + def build_algorithm(self, algorithm: str): """Build algorithm instance based on configuration""" if algorithm == 'q_learning': # Get Q-learning specific config - q_config = self.config.get('q_learning', {}) + q_config = self._merge_shared_config(self.config.get('q_learning', {})) # Calculate action_dim from speed actions (algorithm-specific) speed_actions = q_config.get('speed_actions', [0, 15, 30, 45, 60]) @@ -45,8 +74,8 @@ def build_algorithm(self, algorithm: str): elif algorithm == 'dqn': # Get DQN specific config - dqn_config = self.config.get('dqn', {}) - + dqn_config = self._merge_shared_config(self.config.get('dqn', {})) + # Calculate action_dim from speed actions (algorithm-specific) speed_actions = dqn_config.get('speed_actions', [0, 15, 30, 45, 60]) action_dim = len(speed_actions) @@ -57,20 +86,30 @@ def build_algorithm(self, algorithm: str): return DQNAlgorithm(dqn_config, state_dim, action_dim) elif algorithm == 'td3': - # Get TD3 specific config - td3_config = self.config.get('td3', {}) - + # Get TD3 specific config with shared tensorboard config + td3_config = self._merge_shared_config(self.config.get('td3', {})) + # TD3 uses continuous states state_dim = self.config.get('state_dim', 7) # Use configured state dimension action_dim = self.config.get('action_dim', 1) return TD3Algorithm(td3_config, state_dim, action_dim) + elif algorithm == 'mappo': + # MAPPO - Multi-Agent PPO with CTDE + mappo_config = self._merge_shared_config(self.config.get('mappo', {})) + state_dim = self.config.get('state_dim', 44) # Same as TD3 + action_dim = self.config.get('action_dim', 1) + return MAPPOAlgorithm(mappo_config, state_dim, action_dim) + elif algorithm == 'ppo': - # Placeholder - implement PPO later + # Placeholder - implement single-agent PPO later raise NotImplementedError("PPO not implemented yet") elif algorithm == 'sac': - # Placeholder - implement SAC later - raise NotImplementedError("SAC not implemented yet") + # SAC - Soft Actor-Critic with auto-tuning entropy + sac_config = self._merge_shared_config(self.config.get('sac', {})) + state_dim = self.config.get('state_dim', 8) # Default 8D for simplified state + action_dim = self.config.get('action_dim', 1) + return SACAlgorithm(sac_config, state_dim, action_dim) elif algorithm == 'none': # No MARL algorithm - for baseline agents logger.info("No MARL algorithm initialized (baseline agent mode)") @@ -104,33 +143,50 @@ def compute_actions(self, observations: Dict, training: bool = True) -> Dict[int # Compute actions (speeds) for each agent target_speeds = {} - # Handle TD3 differently due to multi-agent structure - if isinstance(self.algorithm, TD3Algorithm): + # Handle multi-agent algorithms (TD3, MAPPO, SAC) differently + if isinstance(self.algorithm, (TD3Algorithm, MAPPOAlgorithm, SACAlgorithm)): for agent_id_str, multi_agent_data in states.items(): - # Convert string agent_id to int for consistent key types - agent_id_int = int(agent_id_str) if isinstance(agent_id_str, str) else agent_id_str - - # Extract all agent states for TD3 + # Keep agent_id as-is (can be string in SUMO or int in CARLA) + agent_id = agent_id_str + + # Extract all agent states for multi-agent algorithms all_agent_states = multi_agent_data['all_states'] - action = self.algorithm.select_action(all_agent_states, agent_id_str, training=training) - speed = self._compute_td3_action(action, agent_id_int) - + action = self.algorithm.select_action(all_agent_states, agent_id, training=training) + + # Compute speed based on algorithm type + if isinstance(self.algorithm, TD3Algorithm): + speed = self._compute_td3_action(action, agent_id) + elif isinstance(self.algorithm, SACAlgorithm): + speed = self._compute_sac_action(action, agent_id) + else: # MAPPO + speed = self._compute_mappo_action(action, agent_id) + # If speed is None (warmup phase), skip this agent to use vanilla agent if speed is None: - logger.debug(f"TD3: Agent {agent_id_int} in warmup phase, using vanilla agent") + algo_name = type(self.algorithm).__name__.replace('Algorithm', '') + logger.debug(f"{algo_name}: Agent {agent_id} in warmup phase, using vanilla agent") # Track vanilla agent's current speed for memory storage - if agent_id_int in observations: - vanilla_speed = observations[agent_id_int].get('speed', 45.0) - self.last_actions[agent_id_int] = vanilla_speed # Track for TD3 learning + # Handle key type mismatch: extractor uses str keys, but CARLA uses int keys + obs_key = agent_id + if agent_id not in observations: + # Try integer key for CARLA compatibility + try: + obs_key = int(agent_id) + except (ValueError, TypeError): + pass + if obs_key in observations: + vanilla_speed = observations[obs_key].get('speed', 45.0) + self.last_actions[agent_id] = vanilla_speed continue # Don't add to target_speeds, let vanilla agent handle it - - # Clamp speed to TD3 action bounds + + # Clamp speed to algorithm action bounds max_action = self.algorithm.max_action clamped_speed = max(0.0, min(max_action, speed)) - target_speeds[agent_id_int] = clamped_speed - + target_speeds[agent_id] = clamped_speed + # Log the target speed assignment - logger.debug(f"TD3: Agent {agent_id_int} target speed set to {clamped_speed:.2f} km/h") + algo_name = type(self.algorithm).__name__.replace('Algorithm', '') + logger.debug(f"{algo_name}: Agent {agent_id} target speed set to {clamped_speed:.2f} km/h") else: # Handle single-agent algorithms (Q-learning, DQN) for agent_id, state in states.items(): @@ -178,7 +234,11 @@ def update(self, rewards: Dict, observations: Dict, next_observations: Dict): self._update_dqn(rewards, observations, next_observations) elif isinstance(self.algorithm, TD3Algorithm): self._update_td3(rewards, observations, next_observations) - + elif isinstance(self.algorithm, MAPPOAlgorithm): + self._update_mappo(rewards, observations, next_observations) + elif isinstance(self.algorithm, SACAlgorithm): + self._update_sac(rewards, observations, next_observations) + except Exception as e: logger.error(f"Error updating MARL algorithm: {e}") import traceback @@ -299,40 +359,259 @@ def _update_td3(self, rewards: Dict, observations: Dict, next_observations: Dict """TD3 specific update logic.""" states = self.observation_extractor.extract(observations) next_states = self.observation_extractor.extract(next_observations) - + + # Debug: Log key availability periodically + if hasattr(self, '_update_count'): + self._update_count += 1 + else: + self._update_count = 0 + + transitions_stored = 0 + # Store transitions for each agent for agent_id_str in states.keys(): - # Convert string agent_id back to int for rewards/actions lookup - agent_id = int(agent_id_str) - - if (agent_id in rewards and - agent_id_str in next_states and - agent_id in self.last_actions): - - # Get multi-agent observations for TD3 - multi_agent_obs = states[agent_id_str]['all_states'] - next_multi_agent_obs = next_states[agent_id_str]['all_states'] - - action = self.last_actions[agent_id] - reward = rewards[agent_id] - done = agent_id_str not in next_states - - # Store multi-agent transition (use string agent_id for TD3) - self.algorithm.store_transition( - multi_agent_obs, agent_id_str, action, reward, - next_multi_agent_obs, done - ) - + # Use agent_id_str directly (extractor always uses string keys) + agent_id = agent_id_str + + # Handle key type mismatch: extractor uses str keys, but CARLA uses int keys + # Try to find the reward key (might be int or str) + reward_key = agent_id + if agent_id not in rewards: + try: + reward_key = int(agent_id) + except (ValueError, TypeError): + pass + + # Check each condition + in_rewards = reward_key in rewards + in_next_states = agent_id in next_states # next_states uses str keys from extractor + in_last_actions = agent_id in self.last_actions # last_actions uses str keys + + if not (in_rewards and in_next_states and in_last_actions): + if self._update_count % 100 == 0: # Log periodically + logger.debug(f"TD3 Update: Agent {agent_id} skipped - " + f"in_rewards={in_rewards}, in_next_states={in_next_states}, " + f"in_last_actions={in_last_actions}") + continue + + # Get multi-agent observations for TD3 + multi_agent_obs = states[agent_id]['all_states'] + next_multi_agent_obs = next_states[agent_id]['all_states'] + + action = self.last_actions[agent_id] + reward = rewards[reward_key] # Use the correct key type for rewards + done = agent_id not in next_states + + # Store multi-agent transition + self.algorithm.store_transition( + multi_agent_obs, agent_id, action, reward, + next_multi_agent_obs, done + ) + transitions_stored += 1 + + # Log transition storage progress periodically + if self._update_count % 100 == 0: + logger.debug(f"TD3 Update #{self._update_count}: Stored {transitions_stored} transitions, " + f"buffer size={len(self.algorithm.memory)}") + + self.algorithm.update() + + # --------------------------------------------------------------------- # + # MAPPO specific methods + # --------------------------------------------------------------------- # + def _compute_mappo_action(self, action, agent_id: int): + """Compute speed from MAPPO action (direct continuous speed value).""" + # MAPPO always returns an action (no warmup phase like TD3) + if action is None: + return None # Safety fallback + + # MAPPO returns target speed directly (continuous action) + speed = float(action) + + # Store the action for MAPPO updates + self.last_actions[agent_id] = speed + + return speed + + def _update_mappo(self, rewards: Dict, observations: Dict, next_observations: Dict): + """MAPPO specific update logic.""" + states = self.observation_extractor.extract(observations) + next_states = self.observation_extractor.extract(next_observations) + + # Debug: Log key availability periodically + if hasattr(self, '_mappo_update_count'): + self._mappo_update_count += 1 + else: + self._mappo_update_count = 0 + + transitions_stored = 0 + + # Store transitions for each agent + for agent_id_str in states.keys(): + agent_id = agent_id_str + + # Handle key type mismatch + reward_key = agent_id + if agent_id not in rewards: + try: + reward_key = int(agent_id) + except (ValueError, TypeError): + pass + + # Check conditions + in_rewards = reward_key in rewards + in_next_states = agent_id in next_states + in_last_actions = agent_id in self.last_actions + + if not (in_rewards and in_next_states and in_last_actions): + if self._mappo_update_count % 100 == 0: + logger.debug(f"MAPPO Update: Agent {agent_id} skipped - " + f"in_rewards={in_rewards}, in_next_states={in_next_states}, " + f"in_last_actions={in_last_actions}") + continue + + # Get multi-agent observations for MAPPO + multi_agent_obs = states[agent_id]['all_states'] + next_multi_agent_obs = next_states[agent_id]['all_states'] + + action = self.last_actions[agent_id] + reward = rewards[reward_key] + done = agent_id not in next_states + + # Store transition + self.algorithm.store_transition( + multi_agent_obs, agent_id, action, reward, + next_multi_agent_obs, done + ) + transitions_stored += 1 + + # Log progress periodically + if self._mappo_update_count % 100 == 0: + logger.debug(f"MAPPO Update #{self._mappo_update_count}: Stored {transitions_stored} transitions, " + f"buffer size={len(self.algorithm.rollout_buffer)}") + + # Perform PPO update (on-policy: updates when enough data collected) + self.algorithm.update() + + # --------------------------------------------------------------------- # + # SAC specific methods + # --------------------------------------------------------------------- # + def _compute_sac_action(self, action, agent_id: int): + """Compute speed from SAC action (direct continuous speed value).""" + # During warmup, SAC returns random action (not None) + if action is None: + return None # Safety fallback + + # SAC returns target speed directly (continuous action) + speed = float(action) + + # Store the action for SAC updates + self.last_actions[agent_id] = speed + + return speed + + def _update_sac(self, rewards: Dict, observations: Dict, next_observations: Dict): + """SAC specific update logic.""" + states = self.observation_extractor.extract(observations) + next_states = self.observation_extractor.extract(next_observations) + + # Debug: Log key availability periodically + if hasattr(self, '_sac_update_count'): + self._sac_update_count += 1 + else: + self._sac_update_count = 0 + + transitions_stored = 0 + + # Store transitions for each agent + for agent_id_str in states.keys(): + agent_id = agent_id_str + + # Handle key type mismatch + reward_key = agent_id + if agent_id not in rewards: + try: + reward_key = int(agent_id) + except (ValueError, TypeError): + pass + + # Check conditions + in_rewards = reward_key in rewards + in_next_states = agent_id in next_states + in_last_actions = agent_id in self.last_actions + + if not (in_rewards and in_next_states and in_last_actions): + if self._sac_update_count % 100 == 0: + logger.debug(f"SAC Update: Agent {agent_id} skipped - " + f"in_rewards={in_rewards}, in_next_states={in_next_states}, " + f"in_last_actions={in_last_actions}") + continue + + # Get multi-agent observations for SAC + multi_agent_obs = states[agent_id]['all_states'] + next_multi_agent_obs = next_states[agent_id]['all_states'] + + action = self.last_actions[agent_id] + reward = rewards[reward_key] + done = agent_id not in next_states + + # Store transition + self.algorithm.store_transition( + multi_agent_obs, agent_id, action, reward, + next_multi_agent_obs, done + ) + transitions_stored += 1 + + # Log progress periodically + if self._sac_update_count % 100 == 0: + logger.debug(f"SAC Update #{self._sac_update_count}: Stored {transitions_stored} transitions, " + f"buffer size={len(self.algorithm.memory)}") + + # Perform SAC update (off-policy) self.algorithm.update() # --------------------------------------------------------------------- # # Algorithm interface methods (strict implementation required) # --------------------------------------------------------------------- # - def reset_episode(self): + def reset_episode(self, episode_metrics: Dict = None): """Reset algorithm for new episode.""" if self.algorithm is None: logger.info("Episode reset skipped for baseline agent") return + + # Clear last_actions to prevent stale data accumulation + self.last_actions.clear() + + # Log episode metrics to TensorBoard if available + if episode_metrics and hasattr(self.algorithm, 'log_episode_metrics'): + # Extract traffic metrics for separate logging + traffic_metrics = { + 'avg_speed': episode_metrics.get('avg_speed', 0.0), + 'speed_std': episode_metrics.get('speed_std', 0.0), + 'speed_variance': episode_metrics.get('speed_variance', 0.0), + 'min_speed': episode_metrics.get('min_speed', 0.0), + 'max_speed': episode_metrics.get('max_speed', 0.0), + 'speed_smoothness': episode_metrics.get('speed_smoothness', 0.0), + 'avg_step_speed': episode_metrics.get('avg_step_speed', 0.0), + 'avg_agent_speed_var': episode_metrics.get('avg_agent_speed_var', 0.0), + # Target speed metrics (RL-commanded speeds) + 'target_speed_mean': episode_metrics.get('target_speed_mean', 0.0), + 'target_speed_max': episode_metrics.get('target_speed_max', 0.0), + 'target_speed_min': episode_metrics.get('target_speed_min', 0.0), + # Throughput metric (vehicles per hour) + 'throughput': episode_metrics.get('throughput', 0.0), + } + + self.algorithm.log_episode_metrics( + episode_reward=episode_metrics.get('total_reward', 0.0), + episode_length=episode_metrics.get('episode_length', episode_metrics.get('step_length', 0)), + success_rate=episode_metrics.get('success_rate', 0.0), + collision_rate=episode_metrics.get('collision_rate', 0.0), + near_miss_count=episode_metrics.get('near_miss_count', 0), + ttc_violation_rate=episode_metrics.get('ttc_violation_rate', 0.0), + traffic_metrics=traffic_metrics + ) + self.algorithm.reset_episode() logger.info(f"Episode reset for {self.algorithm_name}") @@ -342,6 +621,11 @@ def get_training_metrics(self) -> Dict[str, Any]: return {"algorithm_type": "none", "epsilon": "N/A", "training_mode": False} return self.algorithm.get_training_info() + def close(self): + """Close algorithm resources (e.g., TensorBoard writer).""" + if self.algorithm is not None and hasattr(self.algorithm, 'close'): + self.algorithm.close() + def save_checkpoint(self, filepath: str): """Save model checkpoint.""" if self.algorithm is None: diff --git a/opencda_marl/core/marl/metrics.py b/opencda_marl/core/marl/metrics.py index 0aef63c..aea677f 100644 --- a/opencda_marl/core/marl/metrics.py +++ b/opencda_marl/core/marl/metrics.py @@ -1,13 +1,54 @@ -from typing import Dict, Any +from typing import Dict, Any, Optional, List +from collections import deque import numpy as np +import pickle +import os +from loguru import logger class TrainingMetrics: - """Track and compute training metrics.""" + """Track and compute training metrics including traffic performance. + + Uses rolling windows for real-time stats and periodic file export for full history. + """ + + def __init__(self, export_interval: int = 100, export_dir: str = "metrics_history"): + """ + Initialize metrics tracker. + + Args: + export_interval: Export history to file every N episodes (0 = disabled) + export_dir: Directory to save history files + """ + # Rolling windows for real-time stats (bounded memory) + self.episode_rewards: deque = deque(maxlen=100) + self.episode_states: deque = deque(maxlen=100) + + # Traffic performance history (bounded) + self.episode_avg_speeds: deque = deque(maxlen=100) + self.episode_speed_variances: deque = deque(maxlen=100) + + # Full history for file export (cleared after export) + self._full_history = { + 'rewards': [], + 'avg_speeds': [], + 'speed_variances': [], + 'episode_lengths': [], + 'success_counts': [], + 'collision_counts': [], + 'timeout_counts': [], # Track timeout vehicles + 'near_miss_counts': [] # Track near-miss events for learning analysis + } + + # Export configuration + self.export_interval = export_interval + self.export_dir = export_dir + self._episode_counter = 0 + + # Create export directory if needed + if export_interval > 0: + os.makedirs(export_dir, exist_ok=True) - def __init__(self): - self.episode_rewards = [] - self.episode_states = [] self.reset() def reset(self): @@ -16,12 +57,34 @@ def reset(self): self.current_length = 0 self.current_total_reward = 0.0 self.agent_rewards = {} + self.collisions = 0 + self.successes = 0 + self.timeouts = 0 # Track timeout vehicles + + # Track step when LAST vehicle completes (for accurate episode length) + self.last_completion_step = 0 + + # Traffic performance tracking (per episode) + self.step_speeds: List[float] = [] # All speeds at each step + self.step_avg_speeds: List[float] = [] # Average speed per step + self.agent_speeds: Dict[int, List[float]] = {} # Speed history per agent + self.step_target_speeds: List[float] = [] # Target (commanded) speeds for comparison # --------------------------------------------------------------------- # # Main steps for updating metrics # --------------------------------------------------------------------- # - def update_step(self, rewards: Dict[int, float]): - """Update metrics for current step.""" + def update_step(self, rewards: Dict[int, float], observations: Optional[Dict] = None, + target_speeds: Optional[Dict[int, float]] = None, + step_successes: int = 0): + """ + Update metrics for current step. + + Args: + rewards: Dictionary of agent_id -> reward + observations: Optional dictionary of agent observations (containing speed data) + target_speeds: Optional dictionary of agent_id -> RL-commanded target speed (km/h) + step_successes: Number of vehicles that succeeded this step (for tracking last completion) + """ self.current_reward = sum(rewards.values()) if rewards else 0.0 self.current_total_reward += self.current_reward self.current_length += 1 @@ -30,6 +93,66 @@ def update_step(self, rewards: Dict[int, float]): self.agent_rewards[agent_id] = 0.0 self.agent_rewards[agent_id] += reward + # Track step when vehicles complete (for accurate episode length) + if step_successes > 0: + self.last_completion_step = self.current_length + + # Track traffic performance metrics if observations provided + if observations: + self._update_traffic_metrics(observations) + + # Track RL-commanded target speeds directly (more reliable than observations) + # This captures what the RL algorithm actually commanded, not adapter cached values + if target_speeds: + for agent_id, ts in target_speeds.items(): + if ts is not None and ts > 0: + self.step_target_speeds.append(ts) + + def _update_traffic_metrics(self, observations: Dict): + """ + Update traffic performance metrics from observations. + + Args: + observations: Dictionary of agent observations + """ + step_speeds_list = [] + step_target_speeds_list = [] + + for agent_id, obs in observations.items(): + # Extract speed (handle different observation formats) + speed = None + target_speed = None + if isinstance(obs, dict): + speed = obs.get('speed') + target_speed = obs.get('target_speed') # Track commanded speed + elif hasattr(obs, '__getitem__'): + # Numpy array or list - speed is typically index 2 in 7D features + try: + speed = float(obs[2]) if len(obs) > 2 else None + except (IndexError, TypeError): + pass + + if speed is not None: + step_speeds_list.append(speed) + + # Track per-agent speed history + if agent_id not in self.agent_speeds: + self.agent_speeds[agent_id] = [] + self.agent_speeds[agent_id].append(speed) + + # Filter out 0.0 values (uninitialized or stopped vehicles) + if target_speed is not None and target_speed > 0: + step_target_speeds_list.append(target_speed) + + # Store step-level speed data + if step_speeds_list: + self.step_speeds.extend(step_speeds_list) + self.step_avg_speeds.append(np.mean(step_speeds_list)) + + # Store target speeds + if step_target_speeds_list: + self.step_target_speeds.extend(step_target_speeds_list) + # --------------------------------------------------------------------- # # Public Methods # --------------------------------------------------------------------- # @@ -38,43 +161,259 @@ def get_agent_reward(self, agent_id: int) -> float: return self.agent_rewards.get(agent_id, 0.0) def get_current_metrics(self) -> Dict[str, Any]: - """Get metrics for current episode.""" + """Get metrics for current episode including traffic performance.""" if len(self.agent_rewards) == 0: avg_reward = 0.0 else: avg_reward = self.current_total_reward / \ len(self.agent_rewards) + # Compute traffic performance metrics for current episode + traffic_metrics = self._compute_traffic_metrics() + + # Compute success/collision/timeout rates from tracked counts + # Include timeout vehicles in total to properly account for all spawned vehicles + total_vehicles = self.successes + self.collisions + self.timeouts + success_rate = (self.successes / total_vehicles * 100) if total_vehicles > 0 else 0.0 + collision_rate = (self.collisions / total_vehicles * 100) if total_vehicles > 0 else 0.0 + timeout_rate = (self.timeouts / total_vehicles * 100) if total_vehicles > 0 else 0.0 + if not self.episode_rewards: - return { + metrics = { 'step_reward': self.current_reward, 'step_length': self.current_length, 'total_reward': self.current_total_reward, - 'avg_reward': avg_reward + 'avg_reward': avg_reward, + 'success_rate': success_rate, + 'collision_rate': collision_rate, + 'timeout_rate': timeout_rate, + 'successes': self.successes, + 'collisions': self.collisions, + 'timeouts': self.timeouts, } + metrics.update(traffic_metrics) + return metrics - # Compute running averages - window_size = min(10, len(self.episode_rewards)) - recent_rewards = self.episode_rewards[-window_size:] + # Compute running averages (convert deque to list for slicing) + rewards_list = list(self.episode_rewards) + window_size = min(10, len(rewards_list)) + recent_rewards = rewards_list[-window_size:] - return { + metrics = { 'step_reward': self.current_reward, 'step_length': self.current_length, 'total_reward': self.current_total_reward, 'avg_reward': avg_reward, + 'success_rate': success_rate, + 'collision_rate': collision_rate, + 'timeout_rate': timeout_rate, + 'successes': self.successes, + 'collisions': self.collisions, + 'timeouts': self.timeouts, f'mean_reward_episode_{window_size}': float(np.mean(recent_rewards)), f'std_reward_episode_{window_size}': float(np.std(recent_rewards)), - 'max_reward_episode': max(self.episode_rewards), - 'total_episodes': len(self.episode_rewards), + 'max_reward_episode': max(rewards_list), + 'total_episodes': len(rewards_list), } + metrics.update(traffic_metrics) + + # Add multi-episode traffic trends if available + if len(self.episode_avg_speeds) > 0: + metrics['mean_episode_speed'] = float(np.mean(self.episode_avg_speeds)) + metrics['mean_episode_speed_var'] = float(np.mean(self.episode_speed_variances)) + + return metrics + + def _compute_traffic_metrics(self) -> Dict[str, float]: + """ + Compute traffic performance metrics for current episode. + + Returns: + Dictionary with traffic performance metrics + """ + metrics = {} + + # Average speed across all agents and steps + if self.step_speeds: + metrics['avg_speed'] = float(np.mean(self.step_speeds)) + metrics['speed_std'] = float(np.std(self.step_speeds)) + metrics['speed_variance'] = float(np.var(self.step_speeds)) + metrics['min_speed'] = float(np.min(self.step_speeds)) + metrics['max_speed'] = float(np.max(self.step_speeds)) + else: + metrics['avg_speed'] = 0.0 + metrics['speed_std'] = 0.0 + metrics['speed_variance'] = 0.0 + metrics['min_speed'] = 0.0 + metrics['max_speed'] = 0.0 + + # Target (commanded) speed metrics - for comparing RL output vs actual vehicle speed + if self.step_target_speeds: + metrics['target_speed_mean'] = float(np.mean(self.step_target_speeds)) + metrics['target_speed_max'] = float(np.max(self.step_target_speeds)) + metrics['target_speed_min'] = float(np.min(self.step_target_speeds)) + else: + metrics['target_speed_mean'] = 0.0 + metrics['target_speed_max'] = 0.0 + metrics['target_speed_min'] = 0.0 + + # Speed smoothness (variance of step-average speeds = how stable traffic flow is) + if self.step_avg_speeds: + metrics['speed_smoothness'] = float(np.var(self.step_avg_speeds)) + # Lower variance = smoother flow + metrics['avg_step_speed'] = float(np.mean(self.step_avg_speeds)) + else: + metrics['speed_smoothness'] = 0.0 + metrics['avg_step_speed'] = 0.0 + + # Per-agent speed consistency (how consistent each agent maintains speed) + if self.agent_speeds: + agent_speed_vars = [] + for agent_id, speeds in self.agent_speeds.items(): + if len(speeds) > 1: + agent_speed_vars.append(np.var(speeds)) + if agent_speed_vars: + metrics['avg_agent_speed_var'] = float(np.mean(agent_speed_vars)) + else: + metrics['avg_agent_speed_var'] = 0.0 + else: + metrics['avg_agent_speed_var'] = 0.0 + + return metrics def finish_episode(self, states: Dict[str, Any]): """Finish current episode and compute metrics.""" + self._episode_counter += 1 + + # Update success/collision/timeout counters from episode states + episode_successes = states.get('success', 0) + episode_collisions = states.get('collision', 0) + episode_timeouts = states.get('active_agents', 0) # Active agents at episode end = timeouts + episode_near_misses = states.get('near_miss_count', 0) + self.successes += episode_successes + self.collisions += episode_collisions + self.timeouts += episode_timeouts + + # Compute traffic metrics first + traffic_metrics = self._compute_traffic_metrics() + avg_speed = traffic_metrics.get('avg_speed', 0.0) + speed_variance = traffic_metrics.get('speed_variance', 0.0) + + # Calculate actual episode length: step when LAST vehicle reaches destination + # If no vehicles completed, use total simulation steps as fallback + effective_episode_length = self.last_completion_step if self.last_completion_step > 0 else self.current_length + + # Calculate throughput: successful vehicles per hour + # Formula: (successes / effective_steps / fixed_dt) * 3600 + fixed_dt = states.get('fixed_dt', 0.05) # 20 FPS = 0.05s per step + if effective_episode_length > 0 and fixed_dt > 0: + vehicles_per_second = episode_successes / effective_episode_length / fixed_dt + throughput = vehicles_per_second * 3600 # Convert to vehicles per hour + else: + throughput = 0.0 + + # Add to rolling windows (bounded memory) self.episode_rewards.append(self.current_total_reward) self.episode_states.append(states) + self.episode_avg_speeds.append(avg_speed) + self.episode_speed_variances.append(speed_variance) + + # Add to full history for export (use effective length, not total steps) + self._full_history['rewards'].append(self.current_total_reward) + self._full_history['avg_speeds'].append(avg_speed) + self._full_history['speed_variances'].append(speed_variance) + self._full_history['episode_lengths'].append(effective_episode_length) + self._full_history['success_counts'].append(episode_successes) + self._full_history['collision_counts'].append(episode_collisions) + self._full_history['timeout_counts'].append(episode_timeouts) + self._full_history['near_miss_counts'].append(episode_near_misses) + + # Check if it's time to export + if self.export_interval > 0 and self._episode_counter % self.export_interval == 0: + self.export_history() + metrics = self.get_current_metrics() metrics.update({ - 'episode_states': states + 'episode_states': states, + 'near_miss_count': episode_near_misses, # Add for TensorBoard logging + 'ttc_violation_rate': states.get('ttc_violation_rate', 0.0), # % of TTC checks with violations + 'throughput': throughput, # Vehicles per hour + 'episode_length': effective_episode_length, # Step when LAST vehicle completed + 'total_simulation_steps': self.current_length # Total steps for reference }) self.reset() return metrics + + def export_history(self, filepath: str = None): + """ + Export full history to file and clear memory. + + Args: + filepath: Optional custom filepath. If None, auto-generates based on episode count. + """ + if not self._full_history['rewards']: + logger.debug("No history to export") + return + + if filepath is None: + filepath = os.path.join( + self.export_dir, + f"metrics_history_ep{self._episode_counter}.pkl" + ) + + try: + with open(filepath, 'wb') as f: + pickle.dump(self._full_history, f) + + exported_count = len(self._full_history['rewards']) + logger.info(f"Exported {exported_count} episodes to {filepath}") + + # Clear full history to free memory + self._full_history = { + 'rewards': [], + 'avg_speeds': [], + 'speed_variances': [], + 'episode_lengths': [], + 'success_counts': [], + 'collision_counts': [], + 'timeout_counts': [], + 'near_miss_counts': [] + } + except Exception as e: + logger.error(f"Failed to export metrics history: {e}") + + def get_traffic_statistics(self) -> Dict[str, Any]: + """ + Get comprehensive traffic performance statistics for paper reporting. + + Returns: + Dictionary with traffic performance metrics across all episodes + """ + stats = { + 'total_episodes': len(self.episode_avg_speeds), + } + + if len(self.episode_avg_speeds) > 0: + stats.update({ + 'mean_avg_speed': float(np.mean(self.episode_avg_speeds)), + 'std_avg_speed': float(np.std(self.episode_avg_speeds)), + 'mean_speed_variance': float(np.mean(self.episode_speed_variances)), + 'std_speed_variance': float(np.std(self.episode_speed_variances)), + 'episode_avg_speeds': list(self.episode_avg_speeds), + 'episode_speed_variances': list(self.episode_speed_variances), + }) + + # Trend analysis + if len(self.episode_avg_speeds) > 1: + speed_trend = np.polyfit( + range(len(self.episode_avg_speeds)), + self.episode_avg_speeds, 1 + )[0] + var_trend = np.polyfit( + range(len(self.episode_speed_variances)), + self.episode_speed_variances, 1 + )[0] + stats['speed_trend'] = float(speed_trend) # km/h per episode + stats['variance_trend'] = float(var_trend) # Decreasing = improving smoothness + + return stats diff --git a/opencda_marl/core/safety/marl_safety_manager.py b/opencda_marl/core/safety/marl_safety_manager.py index b388b15..7af5683 100644 --- a/opencda_marl/core/safety/marl_safety_manager.py +++ b/opencda_marl/core/safety/marl_safety_manager.py @@ -24,7 +24,11 @@ def __init__(self, cav_world, vehicle, params): """ super().__init__(cav_world, vehicle, params) - # Only replace the collision sensor (first sensor) with our version + # Destroy the original CollisionSensor before replacing + # to prevent orphaned sensors + self.sensors[0].destroy() + + # Replace with our MARL version self.sensors[0] = MARLCollisionSensor( vehicle, params['collision_sensor']) diff --git a/opencda_marl/core/traffic/sumo_adapter.py b/opencda_marl/core/traffic/sumo_adapter.py new file mode 100644 index 0000000..3cc197f --- /dev/null +++ b/opencda_marl/core/traffic/sumo_adapter.py @@ -0,0 +1,701 @@ +''' +Author : AXIBA leolihao@arizona.edu +Date : 2025-11-17 +FilePath : /OpenCDA-MARL/opencda_marl/core/traffic/sumo_adapter.py +Description : Adapter to make SUMO network compatible with MARLPlanner. + Converts SUMO network topology to CARLA-like waypoint structure. +Copyright (c) 2025 by AXIBA (leolihao@arizona.edu), All Rights Reserved. +''' +import math +import traci +from typing import Dict, List, Tuple, Any, Optional +from loguru import logger +from omegaconf import DictConfig + + +# Mock CARLA classes for compatibility +class Location: + """Mock of carla.Location with arithmetic operations.""" + + def __init__(self, x: float = 0.0, y: float = 0.0, z: float = 0.0): + self.x = float(x) + self.y = float(y) + self.z = float(z) + + def __add__(self, other): + """Add two locations.""" + return Location(self.x + other.x, self.y + other.y, self.z + other.z) + + def __sub__(self, other): + """Subtract two locations.""" + return Location(self.x - other.x, self.y - other.y, self.z - other.z) + + def __repr__(self): + return f"Location(x={self.x:.2f}, y={self.y:.2f}, z={self.z:.2f})" + + +class Rotation: + """Mock of carla.Rotation.""" + + def __init__(self, pitch: float = 0.0, yaw: float = 0.0, roll: float = 0.0): + self.pitch = float(pitch) + self.yaw = float(yaw) + self.roll = float(roll) + + def __repr__(self): + return f"Rotation(pitch={self.pitch:.2f}, yaw={self.yaw:.2f}, roll={self.roll:.2f})" + + +class Transform: + """Mock of carla.Transform.""" + + def __init__(self, location: Location = None, rotation: Rotation = None): + self.location = location if location else Location() + self.rotation = rotation if rotation else Rotation() + + def __repr__(self): + return f"Transform({self.location}, {self.rotation})" + + +class Vector3D: + """Mock of carla.Vector3D.""" + + def __init__(self, x: float = 0.0, y: float = 0.0, z: float = 0.0): + self.x = float(x) + self.y = float(y) + self.z = float(z) + + def __repr__(self): + return f"Vector3D(x={self.x:.2f}, y={self.y:.2f}, z={self.z:.2f})" + + +class BoundingBox: + """Mock of carla.BoundingBox.""" + + def __init__(self, location: Location = None, extent: Vector3D = None): + self.location = location if location else Location() + self.extent = extent if extent else Vector3D() + + def __repr__(self): + return f"BoundingBox({self.location}, {self.extent})" + + +# Mock CARLA enums for compatibility +class LaneType: + """Mock of carla.LaneType""" + Driving = 1 + Sidewalk = 2 + Shoulder = 3 + Biking = 4 + Parking = 5 + Any = 0 + + +class LaneChange: + """Mock of carla.LaneChange""" + NONE = 0 + Right = 1 + Left = 2 + Both = 3 + + +class SumoWaypoint: + """ + CARLA-like waypoint wrapper for SUMO edges/lanes. + Provides same interface as carla.Waypoint for MARLPlanner compatibility. + + IMPORTANT: All positions are stored in CARLA coordinate system for consistency. + SUMO coordinates are converted to CARLA coordinates using netOffset (99.8, 100.0). + """ + + # Network offset for coordinate conversion (SUMO applies this during OpenDRIVE conversion) + NET_OFFSET = (99.80, 100.00) + + def __init__(self, edge_id: str, lane_index: int, position: Tuple[float, float], + road_id: int, lane_id: int, is_junction: bool = False, junction_id: str = None): + self.edge_id = edge_id + self.lane_index = lane_index + + # Convert SUMO coordinates to CARLA coordinates + # CARLA coords = SUMO coords - offset + carla_x = position[0] - self.NET_OFFSET[0] + carla_y = position[1] - self.NET_OFFSET[1] + + self.position = (carla_x, carla_y) # Store in CARLA coords! + self.road_id = road_id # Use edge_id hash for compatibility + self.lane_id = lane_id # SUMO lane index + self.is_junction = is_junction + self.junction_id = junction_id # SUMO junction ID if near junction + self.lane_type = LaneType.Driving # All SUMO lanes are driving lanes + + # CARLA compatibility attributes + self.s = 0.0 # Distance along road (used for _is_same_lane_group) + self.section_id = 0 # SUMO doesn't have sections, use 0 for all + + # Create transform using CARLA coordinates + self.transform = Transform( + location=Location(x=carla_x, y=carla_y, z=0.0), + rotation=Rotation(pitch=0.0, yaw=0.0, roll=0.0) + ) + + def get_left_lane(self): + """Get left lane if exists.""" + # In SUMO, lane 0 is rightmost, increasing index goes left + try: + edge_lanes = traci.edge.getLaneNumber(self.edge_id) + except: + return None + + if self.lane_index < edge_lanes - 1: + new_lane_id = f"{self.edge_id}_{self.lane_index + 1}" + try: + shape = traci.lane.getShape(new_lane_id) + except: + return None + + if shape and len(shape) >= 2: + # IMPORTANT: Use same position index as current waypoint + # If current waypoint is at end (-1), use end of adjacent lane too + # This ensures all lanes have coordinates at the same longitudinal position + position = self.position # Current waypoint position in CARLA coords + + # Convert to SUMO coords to match the longitudinal position + current_sumo_y = position[1] + self.NET_OFFSET[1] + + # Find point in adjacent lane shape closest to current Y position + # For simplicity, use end point if current is near end, else mid point + if abs(current_sumo_y - shape[-1][1]) < 1.0: + # Current waypoint is at end, use end of adjacent lane + lane_point = shape[-1] + else: + # Current waypoint is at mid, use mid of adjacent lane + lane_point = shape[len(shape) // 2] + + return SumoWaypoint( + self.edge_id, self.lane_index + 1, lane_point, + self.road_id, self.lane_id + 1, self.is_junction, self.junction_id + ) + return None + + def get_right_lane(self): + """Get right lane if exists.""" + if self.lane_index > 0: + new_lane_id = f"{self.edge_id}_{self.lane_index - 1}" + try: + shape = traci.lane.getShape(new_lane_id) + except: + return None + + if shape and len(shape) >= 2: + # IMPORTANT: Use same position index as current waypoint + position = self.position # Current waypoint position in CARLA coords + + # Convert to SUMO coords to match the longitudinal position + current_sumo_y = position[1] + self.NET_OFFSET[1] + + # Find point in adjacent lane shape closest to current Y position + if abs(current_sumo_y - shape[-1][1]) < 1.0: + # Current waypoint is at end, use end of adjacent lane + lane_point = shape[-1] + else: + # Current waypoint is at mid, use mid of adjacent lane + lane_point = shape[len(shape) // 2] + + return SumoWaypoint( + self.edge_id, self.lane_index - 1, lane_point, + self.road_id, self.lane_id - 1, self.is_junction, self.junction_id + ) + return None + + @property + def lane_change(self): + """Determine if lane changes are allowed (for MARLPlanner lane ordering).""" + edge_lanes = traci.edge.getLaneNumber(self.edge_id) + + if edge_lanes == 1: + return LaneChange.NONE + elif self.lane_index == 0: + return LaneChange.Left # Rightmost - can only change left + elif self.lane_index == edge_lanes - 1: + return LaneChange.Right # Leftmost - can only change right + else: + return LaneChange.Both # Middle - can change both ways + + def get_junction(self): + """Get junction if this waypoint is at/near a junction.""" + if not self.is_junction or not self.junction_id: + return None + + return SumoJunction(self.junction_id) + + def next(self, distance: float): + """ + Get waypoints ahead of this waypoint along the lane. + + Args: + distance: Distance in meters to move along the lane + + Returns: + List of SumoWaypoint objects (CARLA compatibility - returns list) + """ + lane_id = f"{self.edge_id}_{self.lane_index}" + + try: + # Get lane shape (list of points) + shape = traci.lane.getShape(lane_id) + if not shape or len(shape) < 2: + return [] + + # Current position is middle of lane, compute new position + # For simplicity, return waypoint at end of edge (where it connects to next edge) + end_point = shape[-1] + + # Check if there's a next edge connected + to_junction_id = traci.edge.getToJunction(self.edge_id) + if not to_junction_id: + return [] + + # Get edges leaving this junction + all_edges = traci.edge.getIDList() + next_waypoints = [] + + for edge_id in all_edges: + if edge_id.startswith(':'): + continue + + from_junction = traci.edge.getFromJunction(edge_id) + if from_junction == to_junction_id and edge_id != self.edge_id: + # This edge leaves our junction + num_lanes = traci.edge.getLaneNumber(edge_id) + + # Create waypoint for same lane index if exists + lane_idx = min(self.lane_index, num_lanes - 1) + next_lane_id = f"{edge_id}_{lane_idx}" + next_shape = traci.lane.getShape(next_lane_id) + + if next_shape and len(next_shape) >= 2: + # Waypoint at start of next edge + start_point = next_shape[0] + next_to_junction = traci.edge.getToJunction(edge_id) + + wp = SumoWaypoint( + edge_id, lane_idx, start_point, + hash(edge_id) % 10000, -lane_idx - 1, + is_junction=(next_to_junction is not None and next_to_junction != ''), + junction_id=next_to_junction if next_to_junction else None + ) + next_waypoints.append(wp) + + return next_waypoints + + except Exception as e: + logger.debug(f"Failed to get next waypoint for {self.edge_id}_{self.lane_index}: {e}") + return [] + + def previous(self, distance: float): + """ + Get waypoints behind this waypoint along the lane. + + IMPORTANT: This method now interpolates along the SAME lane shape + to provide proper spawn point distribution across lanes. + + Args: + distance: Distance in meters to move backwards along the lane + + Returns: + List of SumoWaypoint objects (CARLA compatibility - returns list) + """ + lane_id = f"{self.edge_id}_{self.lane_index}" + + try: + # Get lane shape (list of points) + shape = traci.lane.getShape(lane_id) + if not shape or len(shape) < 2: + return [] + + # Calculate total lane length + lane_length = traci.lane.getLength(lane_id) + + # Current position in CARLA coords - convert to SUMO to match shape + current_sumo_x = self.position[0] + self.NET_OFFSET[0] + current_sumo_y = self.position[1] + self.NET_OFFSET[1] + + # Find current position along the lane shape + # For simplicity, if we're near the end, assume we're at the end + # Otherwise, assume we're at the start + current_distance_from_start = 0.0 + if len(shape) == 2: + # Simple 2-point lane - calculate distance from start + start_x, start_y = shape[0] + end_x, end_y = shape[-1] + total_dist = math.sqrt((end_x - start_x)**2 + (end_y - start_y)**2) + current_dist = math.sqrt((current_sumo_x - start_x)**2 + (current_sumo_y - start_y)**2) + current_distance_from_start = current_dist + else: + # Multi-point lane - find closest segment + for i in range(len(shape) - 1): + x1, y1 = shape[i] + x2, y2 = shape[i + 1] + # Simple check: if we're close to end point, we're at the end + if abs(current_sumo_x - x2) < 1.0 and abs(current_sumo_y - y2) < 1.0: + # Sum distances up to this segment + for j in range(i + 1): + x_a, y_a = shape[j] + x_b, y_b = shape[j + 1] + current_distance_from_start += math.sqrt((x_b - x_a)**2 + (y_b - y_a)**2) + break + + # Calculate target distance from start (moving backwards) + target_distance_from_start = current_distance_from_start - distance + + # Clamp to valid range + target_distance_from_start = max(0.0, min(lane_length, target_distance_from_start)) + + # Interpolate position along lane shape + if len(shape) == 2: + # Simple linear interpolation for 2-point lanes + start_x, start_y = shape[0] + end_x, end_y = shape[-1] + total_dist = math.sqrt((end_x - start_x)**2 + (end_y - start_y)**2) + if total_dist > 0: + ratio = target_distance_from_start / total_dist + new_x = start_x + ratio * (end_x - start_x) + new_y = start_y + ratio * (end_y - start_y) + else: + new_x, new_y = start_x, start_y + else: + # Multi-point interpolation + accumulated_dist = 0.0 + new_x, new_y = shape[0] # Default to start + for i in range(len(shape) - 1): + x1, y1 = shape[i] + x2, y2 = shape[i + 1] + segment_length = math.sqrt((x2 - x1)**2 + (y2 - y1)**2) + + if accumulated_dist + segment_length >= target_distance_from_start: + # Target is in this segment + remaining = target_distance_from_start - accumulated_dist + if segment_length > 0: + ratio = remaining / segment_length + new_x = x1 + ratio * (x2 - x1) + new_y = y1 + ratio * (y2 - y1) + else: + new_x, new_y = x1, y1 + break + + accumulated_dist += segment_length + + # Create new waypoint at interpolated position + new_position_sumo = (new_x, new_y) + wp = SumoWaypoint( + self.edge_id, self.lane_index, new_position_sumo, + self.road_id, self.lane_id, self.is_junction, self.junction_id + ) + return [wp] # Return as list for CARLA compatibility + + except Exception as e: + logger.debug(f"Failed to interpolate previous waypoint for {self.edge_id}_{self.lane_index}: {e}") + # Fallback: return empty to avoid breaking the planner + return [] + + +class SumoJunction: + """ + CARLA-like junction wrapper for SUMO junctions. + Provides same interface as carla.Junction for MARLPlanner compatibility. + + IMPORTANT: Junction center is stored in CARLA coordinate system for consistency. + """ + + # Network offset for coordinate conversion (same as SumoWaypoint) + NET_OFFSET = (99.80, 100.00) + + def __init__(self, junction_id: str): + self.id = int(junction_id) if junction_id.isdigit() else hash(junction_id) % 10000 + self.junction_id = junction_id + + # Get junction position and shape (in SUMO coordinates) + pos = traci.junction.getPosition(junction_id) + shape = traci.junction.getShape(junction_id) + + # Calculate bounding box in SUMO coordinates first + if shape: + xs = [p[0] for p in shape] + ys = [p[1] for p in shape] + center_x_sumo = sum(xs) / len(xs) + center_y_sumo = sum(ys) / len(ys) + extent_x = (max(xs) - min(xs)) / 2 + extent_y = (max(ys) - min(ys)) / 2 + else: + center_x_sumo, center_y_sumo = pos + extent_x, extent_y = 10.0, 10.0 # Default size + + # Convert junction center to CARLA coordinates + center_x_carla = center_x_sumo - self.NET_OFFSET[0] + center_y_carla = center_y_sumo - self.NET_OFFSET[1] + + # Create CARLA-like bounding box using CARLA coordinates + self.bounding_box = BoundingBox( + location=Location(x=center_x_carla, y=center_y_carla, z=0.0), + extent=Vector3D(x=extent_x, y=extent_y, z=1.0) + ) + + def get_waypoints(self, lane_type=None): + """ + Get all entry/exit waypoint pairs for this junction. + Returns list of tuples: (entry_waypoint, exit_waypoint) + + Args: + lane_type: Filter by lane type (only Driving is supported for SUMO) + """ + # SUMO only has driving lanes, so lane_type filter has no effect + connections = [] + + # Get all incoming edges to this junction + incoming_edges = [] + all_edges = traci.edge.getIDList() + + for edge_id in all_edges: + # Skip internal edges + if edge_id.startswith(':'): + continue + + # Check if this edge leads to our junction + to_node = traci.edge.getToJunction(edge_id) + if to_node == self.junction_id: + incoming_edges.append(edge_id) + + # Get all outgoing edges from this junction + outgoing_edges = [] + for edge_id in all_edges: + if edge_id.startswith(':'): + continue + + from_node = traci.edge.getFromJunction(edge_id) + if from_node == self.junction_id: + outgoing_edges.append(edge_id) + + # Create waypoint pairs for all connections + for entry_edge in incoming_edges: + num_lanes = traci.edge.getLaneNumber(entry_edge) + for lane_idx in range(num_lanes): + lane_id = f"{entry_edge}_{lane_idx}" + shape = traci.lane.getShape(lane_id) + if not shape: + continue + + # Entry waypoint at end of incoming edge + entry_pos = shape[-1] + entry_wp = SumoWaypoint( + entry_edge, lane_idx, entry_pos, + hash(entry_edge) % 10000, -lane_idx - 1, False + ) + + # Find connected outgoing edges + for exit_edge in outgoing_edges: + exit_num_lanes = traci.edge.getLaneNumber(exit_edge) + for exit_lane_idx in range(exit_num_lanes): + exit_lane_id = f"{exit_edge}_{exit_lane_idx}" + exit_shape = traci.lane.getShape(exit_lane_id) + if not exit_shape: + continue + + # Exit waypoint at start of outgoing edge + exit_pos = exit_shape[0] + exit_wp = SumoWaypoint( + exit_edge, exit_lane_idx, exit_pos, + hash(exit_edge) % 10000, -exit_lane_idx - 1, False + ) + + connections.append((entry_wp, exit_wp)) + + return connections + + +class ActorBlueprint: + """Mock of carla.ActorBlueprint.""" + + def __init__(self, blueprint_id: str): + self.id = blueprint_id + + def __repr__(self): + return f"ActorBlueprint(id='{self.id}')" + + +class BlueprintLibrary: + """Mock of carla.BlueprintLibrary.""" + + def __init__(self): + # Create some default vehicle blueprints for SUMO + self.blueprints = [ + ActorBlueprint('vehicle.audi.a2'), + ActorBlueprint('vehicle.tesla.model3'), + ActorBlueprint('vehicle.volkswagen.t2'), + ActorBlueprint('vehicle.bmw.grandtourer'), + ActorBlueprint('vehicle.toyota.prius'), + ] + + def filter(self, wildcard: str): + """Filter blueprints by wildcard pattern.""" + if wildcard == 'vehicle.*': + return self.blueprints + # Simple pattern matching + filtered = [bp for bp in self.blueprints if wildcard.replace('*', '') in bp.id] + return filtered + + def find(self, blueprint_id: str): + """Find blueprint by exact ID.""" + for bp in self.blueprints: + if bp.id == blueprint_id: + return bp + # If not found, create a new one dynamically + new_bp = ActorBlueprint(blueprint_id) + self.blueprints.append(new_bp) + return new_bp + + def __iter__(self): + return iter(self.blueprints) + + def __len__(self): + return len(self.blueprints) + + +class SumoWorld: + """ + CARLA-like world wrapper for SUMO simulation. + Provides same interface as carla.World for MARLPlanner compatibility. + """ + + def __init__(self): + self._map = SumoMap() + self._blueprint_library = BlueprintLibrary() + + # Mock debug interface (MARLPlanner uses this for visualization) + self.debug = type('Debug', (), { + 'draw_point': lambda *args, **kwargs: None, + 'draw_box': lambda *args, **kwargs: None, + 'draw_line': lambda *args, **kwargs: None, + 'draw_string': lambda *args, **kwargs: None, + })() + + def get_map(self): + """Get the SUMO map (CARLA World interface).""" + return self._map + + def get_blueprint_library(self): + """Get the blueprint library (CARLA World interface).""" + return self._blueprint_library + + +class SumoMap: + """ + CARLA-like map wrapper for SUMO network. + Provides same interface as carla.Map for MARLPlanner compatibility. + """ + + def generate_waypoints(self, distance: float = 2.0) -> List[SumoWaypoint]: + """ + Generate waypoints along all edges in the network. + + Args: + distance: Spacing between waypoints (meters) + + Returns: + List of SumoWaypoint objects + """ + waypoints = [] + + # Get all edges in network + all_edges = traci.edge.getIDList() + + for edge_id in all_edges: + # Skip internal junction edges + if edge_id.startswith(':'): + continue + + # Get number of lanes + num_lanes = traci.edge.getLaneNumber(edge_id) + + # Create waypoints for each lane + for lane_idx in range(num_lanes): + lane_id = f"{edge_id}_{lane_idx}" + shape = traci.lane.getShape(lane_id) + + if not shape or len(shape) < 2: + continue + + # Sample point at middle of lane + mid_idx = len(shape) // 2 + mid_point = shape[mid_idx] + + # Check if this edge leads to a junction + to_junction_id = traci.edge.getToJunction(edge_id) + is_junction = (to_junction_id is not None and to_junction_id != '') + + wp = SumoWaypoint( + edge_id, lane_idx, mid_point, + road_id=hash(edge_id) % 10000, + lane_id=-lane_idx - 1, # Negative like CARLA + is_junction=is_junction, + junction_id=to_junction_id if is_junction else None + ) + waypoints.append(wp) + + logger.debug(f"Generated {len(waypoints)} waypoints from SUMO network") + return waypoints + + +class SumoMARLPlanner: + """ + SUMO-compatible version of MARLPlanner that uses SUMO network topology. + Provides same interface as MARLPlanner but works with SUMO TraCI. + """ + + def __init__(self, config: DictConfig): + """ + Initialize SUMO MARL planner. + + Args: + config: Configuration dictionary with planner parameters + """ + self.config = config + self.world = SumoWorld() + self.map = self.world.get_map() + + # Initialize junctions + self._junctions = self._get_junctions() + + logger.success(f"SumoMARLPlanner initialized with {len(self._junctions)} junctions") + + def _get_junctions(self) -> Dict[int, Dict[str, Any]]: + """ + Analyze SUMO network and extract all junctions with routes. + Uses same logic as MARLPlanner but with SUMO data. + """ + from opencda_marl.core.traffic.planner import MARLPlanner + + # Create temporary MARLPlanner instance with SUMO world adapter + # This reuses all the route generation logic + temp_planner = MARLPlanner(self.world, self.config) + + # Return the junctions (MARLPlanner does all the heavy lifting) + return temp_planner._junctions + + def get_junctions(self) -> Dict[int, Dict[str, Any]]: + """Get all junctions.""" + return self._junctions + + def get_routes(self, junction_id: int) -> Dict[int, Dict]: + """Get all routes for a junction.""" + if junction_id not in self._junctions: + return {} + return self._junctions[junction_id]["routes"] + + def get_route(self, junction_id: int, route_id: int) -> Optional[Dict]: + """Get specific route by ID.""" + routes = self.get_routes(junction_id) + return routes.get(route_id) + + def cleanup(self): + """Cleanup resources.""" + self._junctions.clear() diff --git a/opencda_marl/core/traffic/sumo_spawner.py b/opencda_marl/core/traffic/sumo_spawner.py new file mode 100644 index 0000000..e090f21 --- /dev/null +++ b/opencda_marl/core/traffic/sumo_spawner.py @@ -0,0 +1,412 @@ +''' +Author : AXIBA leolihao@arizona.edu +Date : 2025-11-17 +FilePath : /OpenCDA-MARL/opencda_marl/core/traffic/sumo_spawner.py +Description : SUMO vehicle spawner that uses MARLTrafficManager's SpawnEvent system. + Spawns vehicles in SUMO based on events from traffic manager. +Copyright (c) 2025 by AXIBA (leolihao@arizona.edu), All Rights Reserved. +''' +import traci +import math +from typing import Dict, List, Optional, Tuple +from loguru import logger + +from .events import SpawnEvent + + +class SumoVehicleSpawner: + """ + Spawns vehicles in SUMO using SpawnEvent objects from MARLTrafficManager. + Maintains consistency with CARLA by using same route generation logic. + """ + + def __init__(self): + """Initialize SUMO vehicle spawner.""" + self.spawned_vehicles = {} # {vehicle_id: event} + self.spawn_counter = 0 + self.failed_spawns = [] + + # Get SUMO network offset for coordinate transformation + # SUMO applies netOffset during conversion: sumo_coord = carla_coord + offset + net_boundary = traci.simulation.getNetBoundary() + self.net_offset = self._get_network_offset() + logger.info(f"SUMO network offset: {self.net_offset}") + + # Note: We use SUMO's built-in 'DEFAULT_VEHTYPE' for all vehicles + # No need to create custom vehicle types + + def spawn_vehicle(self, event: SpawnEvent) -> Optional[str]: + """ + Spawn a vehicle in SUMO based on SpawnEvent. + + Args: + event: SpawnEvent containing spawn location, destination, route type, etc. + + Returns: + vehicle_id if successful, None otherwise + """ + # Extract metadata + entry_dir = event.metadata.get('entry_direction', 'unknown') + lane_idx = event.metadata.get('lane_id', event.lane_id) + + # Generate unique vehicle ID + vehicle_id = f"{event.flow_name}_{entry_dir}_{lane_idx}_{self.spawn_counter}" + self.spawn_counter += 1 + + # Extract spawn and destination from event (CARLA coordinates) + spawn_loc = event.transform.location + dest_loc = event.destination.location + + # Transform CARLA coordinates to SUMO coordinates + spawn_x_sumo, spawn_y_sumo = self._carla_to_sumo(spawn_loc.x, spawn_loc.y) + dest_x_sumo, dest_y_sumo = self._carla_to_sumo(dest_loc.x, dest_loc.y) + + # Find compatible edge pair for spawn and destination + # This considers both proximity and routing compatibility + spawn_edge, dest_edge = self._find_compatible_edges( + spawn_x_sumo, spawn_y_sumo, + dest_x_sumo, dest_y_sumo, + event.metadata.get('route_type', 'unknown') + ) + + if not spawn_edge: + logger.warning(f"Could not find spawn edge for {vehicle_id} at CARLA ({spawn_loc.x:.1f}, {spawn_loc.y:.1f}) → SUMO ({spawn_x_sumo:.1f}, {spawn_y_sumo:.1f})") + self.failed_spawns.append(event) + return None + + if not dest_edge: + logger.warning(f"Could not find destination edge for {vehicle_id} at CARLA ({dest_loc.x:.1f}, {dest_loc.y:.1f}) → SUMO ({dest_x_sumo:.1f}, {dest_y_sumo:.1f})") + self.failed_spawns.append(event) + return None + + # Compute route between spawn and destination + try: + route = traci.simulation.findRoute(spawn_edge, dest_edge) + if not route or not route.edges: + route_type = event.metadata.get('route_type', 'unknown') + logger.debug(f"No route found: {spawn_edge} → {dest_edge} (route_type={route_type})") + self.failed_spawns.append(event) + return None + except Exception as e: + route_type = event.metadata.get('route_type', 'unknown') + logger.debug(f"Route computation failed: {spawn_edge} → {dest_edge} (route_type={route_type}): {e}") + self.failed_spawns.append(event) + return None + + # Determine lane index from event (already extracted above) + # Clamp to valid range for this edge + num_lanes = traci.edge.getLaneNumber(spawn_edge) + spawn_lane_idx = min(lane_idx, num_lanes - 1) + + # Get vehicle type from event blueprint (convert CARLA blueprint to SUMO type) + carla_blueprint_id = event.blueprint.id if event.blueprint else 'vehicle.audi.a2' + vtype_id = self._carla_blueprint_to_sumo_type(carla_blueprint_id) + + try: + # Add vehicle to simulation + traci.vehicle.add( + vehID=vehicle_id, + routeID="", # Empty route, we'll set it manually + typeID=vtype_id, + depart='now', + departLane=str(spawn_lane_idx), + departSpeed='max' + ) + + # Set the route + traci.vehicle.setRoute(vehicle_id, route.edges) + + # Set target speed from event + target_speed_kmh = event.target_speed + target_speed_ms = target_speed_kmh / 3.6 # Convert km/h to m/s + traci.vehicle.setSpeed(vehicle_id, target_speed_ms) + + # Store event for tracking + self.spawned_vehicles[vehicle_id] = event + + logger.debug(f"Spawned {vehicle_id} on {spawn_edge} lane {spawn_lane_idx}, " + f"route: {event.metadata.get('route_type', 'unknown')}, " + f"dest: {dest_edge}") + + return vehicle_id + + except traci.exceptions.TraCIException as e: + logger.warning(f"Failed to spawn {vehicle_id}: {e}") + self.failed_spawns.append(event) + return None + + def spawn_vehicles(self, events: List[SpawnEvent]) -> List[str]: + """ + Spawn multiple vehicles from a list of events. + + Args: + events: List of SpawnEvent objects + + Returns: + List of successfully spawned vehicle IDs + """ + spawned_ids = [] + + for event in events: + veh_id = self.spawn_vehicle(event) + if veh_id: + spawned_ids.append(veh_id) + + if spawned_ids: + logger.info(f"Spawned {len(spawned_ids)}/{len(events)} vehicles this step") + + return spawned_ids + + def get_spawn_event(self, vehicle_id: str) -> Optional[SpawnEvent]: + """Get the SpawnEvent for a vehicle.""" + return self.spawned_vehicles.get(vehicle_id) + + def remove_vehicle(self, vehicle_id: str): + """Remove vehicle from tracking.""" + if vehicle_id in self.spawned_vehicles: + del self.spawned_vehicles[vehicle_id] + + def get_failed_spawns(self) -> List[SpawnEvent]: + """Get list of failed spawn attempts.""" + return self.failed_spawns.copy() + + def clear_failed_spawns(self): + """Clear failed spawn list.""" + self.failed_spawns.clear() + + # --------------------------------------------------------------------- # + # Helper Methods + # --------------------------------------------------------------------- # + + def _find_compatible_edges(self, spawn_x: float, spawn_y: float, + dest_x: float, dest_y: float, + route_type: str) -> Tuple[Optional[str], Optional[str]]: + """ + Find compatible edge pair for spawn and destination that can form a valid route. + + This method finds edges that: + 1. Are close to the given coordinates + 2. Can form a valid route (not opposite directions) + 3. Match the expected route type when possible + + Args: + spawn_x: Spawn X coordinate (SUMO) + spawn_y: Spawn Y coordinate (SUMO) + dest_x: Destination X coordinate (SUMO) + dest_y: Destination Y coordinate (SUMO) + route_type: Expected route type (straight, left, right) + + Returns: + Tuple of (spawn_edge, dest_edge) or (None, None) if no valid pair found + """ + # Get candidate edges for spawn and destination + spawn_candidates = self._get_candidate_edges(spawn_x, spawn_y, max_distance=50.0) + dest_candidates = self._get_candidate_edges(dest_x, dest_y, max_distance=50.0) + + if not spawn_candidates or not dest_candidates: + return None, None + + # Try to find a compatible pair + # Prioritize pairs that avoid opposite-direction edges + best_pair = None + best_score = float('inf') + + for spawn_edge, spawn_dist in spawn_candidates: + for dest_edge, dest_dist in dest_candidates: + # Skip opposite direction edges + if spawn_edge == f"-{dest_edge}" or dest_edge == f"-{spawn_edge}": + continue + + # Check if route exists (lightweight check without full computation) + try: + # Quick connectivity check using SUMO's edge connections + from_edges = traci.edge.getIDList() + if spawn_edge in from_edges and dest_edge in from_edges: + # Score based on distance (lower is better) + score = spawn_dist + dest_dist + + if score < best_score: + best_score = score + best_pair = (spawn_edge, dest_edge) + except: + continue + + if best_pair: + return best_pair + + # Fallback: return closest edges even if they might not be compatible + # (routing check will catch issues later) + return spawn_candidates[0][0] if spawn_candidates else None, \ + dest_candidates[0][0] if dest_candidates else None + + def _get_candidate_edges(self, x: float, y: float, max_distance: float = 50.0) -> List[Tuple[str, float]]: + """ + Get list of candidate edges near coordinates, sorted by distance. + + Args: + x: X coordinate (SUMO) + y: Y coordinate (SUMO) + max_distance: Maximum search distance + + Returns: + List of (edge_id, distance) tuples, sorted by distance + """ + candidates = [] + all_edges = traci.edge.getIDList() + + for edge_id in all_edges: + # Skip internal junction edges + if edge_id.startswith(':'): + continue + + # Get edge shape + num_lanes = traci.edge.getLaneNumber(edge_id) + if num_lanes == 0: + continue + + # Check middle lane + lane_id = f"{edge_id}_{num_lanes // 2}" + try: + shape = traci.lane.getShape(lane_id) + except: + continue + + if not shape: + continue + + # Find minimum distance to this edge + min_dist = float('inf') + for point in shape: + dist = math.sqrt((x - point[0])**2 + (y - point[1])**2) + min_dist = min(min_dist, dist) + + if min_dist <= max_distance: + candidates.append((edge_id, min_dist)) + + # Sort by distance + candidates.sort(key=lambda x: x[1]) + return candidates + + def _find_closest_edge(self, x: float, y: float, max_distance: float = 50.0) -> Optional[str]: + """ + Find the closest SUMO edge to given coordinates. + + Args: + x: X coordinate + y: Y coordinate + max_distance: Maximum search distance in meters + + Returns: + Edge ID or None if no edge found + """ + # Get all edges in network + all_edges = traci.edge.getIDList() + + closest_edge = None + min_distance = float('inf') + + for edge_id in all_edges: + # Skip internal junction edges + if edge_id.startswith(':'): + continue + + # Get edge shape (list of (x, y) points) + num_lanes = traci.edge.getLaneNumber(edge_id) + if num_lanes == 0: + continue + + # Check middle lane + lane_id = f"{edge_id}_{num_lanes // 2}" + try: + shape = traci.lane.getShape(lane_id) + except: + continue + + if not shape: + continue + + # Calculate distance to edge + for point in shape: + dist = math.sqrt((x - point[0])**2 + (y - point[1])**2) + if dist < min_distance: + min_distance = dist + closest_edge = edge_id + + # Only return if within max_distance + if min_distance <= max_distance: + return closest_edge + + return None + + def _carla_blueprint_to_sumo_type(self, carla_blueprint_id: str) -> str: + """ + Convert CARLA vehicle blueprint ID to SUMO vehicle type ID. + + CARLA uses detailed blueprint IDs like 'vehicle.audi.a2', 'vehicle.bmw.grandtourer', + while SUMO uses generic type IDs. + + For maximum compatibility, we use SUMO's built-in 'DEFAULT_VEHTYPE' which is + always available in any SUMO simulation. + + Args: + carla_blueprint_id: CARLA blueprint ID (e.g., 'vehicle.audi.a2') + + Returns: + SUMO vehicle type ID ('DEFAULT_VEHTYPE') + """ + # Use SUMO's built-in default vehicle type - always available + return 'DEFAULT_VEHTYPE' + + def _get_network_offset(self) -> tuple: + """ + Get the network offset from SUMO network. + + SUMO applies netOffset during conversion: sumo_coord = carla_coord + offset + The offset is stored in the network file's tag. + + Returns: + (offset_x, offset_y) tuple + """ + # For the intersection network, the offset is (99.80, 100.00) + # This can be read from network boundary or hardcoded + # Since CARLA junction 4 is at (99.80, 99.57) and SUMO junction 4 is also at (99.80, 99.57), + # but SUMO uses positive coordinates (0-200 range), the offset must be applied to spawn points + + # The netOffset from intersection.net.xml is (99.80, 100.00) + return (99.80, 100.00) + + def _carla_to_sumo(self, x: float, y: float) -> tuple: + """ + Transform CARLA coordinates to SUMO coordinates. + + Args: + x: CARLA X coordinate + y: CARLA Y coordinate + + Returns: + (sumo_x, sumo_y) tuple + """ + sumo_x = x + self.net_offset[0] + sumo_y = y + self.net_offset[1] + return (sumo_x, sumo_y) + + def _sumo_to_carla(self, x: float, y: float) -> tuple: + """ + Transform SUMO coordinates to CARLA coordinates. + + Args: + x: SUMO X coordinate + y: SUMO Y coordinate + + Returns: + (carla_x, carla_y) tuple + """ + carla_x = x - self.net_offset[0] + carla_y = y - self.net_offset[1] + return (carla_x, carla_y) + + def cleanup(self): + """Cleanup spawner resources.""" + self.spawned_vehicles.clear() + self.failed_spawns.clear() + self.spawn_counter = 0 diff --git a/opencda_marl/core/traffic/traffic_manager.py b/opencda_marl/core/traffic/traffic_manager.py index f599a6f..026ba6f 100644 --- a/opencda_marl/core/traffic/traffic_manager.py +++ b/opencda_marl/core/traffic/traffic_manager.py @@ -141,14 +141,22 @@ def _generate_and_save_events(self): logger.success(f"Events saved successfully to {save_path}") def _generate_events(self): + logger.debug(f"Generating events for {len(self.active_junctions)} junctions and {len(self.flows)} flows") + logger.debug(f"Active junctions: {self.active_junctions}") + logger.debug(f"Flows: {[f.name for f in self.flows]}") + for j_id in self.active_junctions: + logger.debug(f"Processing junction {j_id}") for flow in self.flows: - events = flow._generate_events(self.vbp, self.planner, + logger.debug(f"Generating events for flow '{flow.name}' at junction {j_id}") + events = flow._generate_events(self.vbp, self.planner, j_id, fix_dlt=self.fix_dlt, base_speed=self.base_speed) + logger.debug(f"Generated {len(events)} events for flow '{flow.name}'") self._events.extend(events) # sort events by spawn_step self._events.sort(key=lambda x: x.spawn_step) + logger.debug(f"Total events generated: {len(self._events)}") def _parse_flows(self) -> List[Dict[str, Any]]: flow_dicts = self.config.get('flows', []) diff --git a/opencda_marl/core/traffic/utils.py b/opencda_marl/core/traffic/utils.py index 8faf5d9..2072b58 100644 --- a/opencda_marl/core/traffic/utils.py +++ b/opencda_marl/core/traffic/utils.py @@ -6,11 +6,27 @@ Copyright (c) 2025 by AXIBA (leolihao@arizona.edu), All Rights Reserved. ''' import carla -from typing import List +from typing import List, Union -def with_z(t: carla.Transform, z: float) -> carla.Transform: - return carla.Transform(t.location + carla.Location(z=float(z)), t.rotation) +def with_z(t: Union[carla.Transform, 'Transform'], z: float) -> Union[carla.Transform, 'Transform']: + """ + Add z-offset to a transform. + Works with both CARLA Transform and SUMO mock Transform. + """ + # Check if this is a SUMO mock Transform (has module 'sumo_adapter') + if hasattr(t, '__class__') and 'sumo_adapter' in t.__class__.__module__: + # SUMO mock object - use mock classes + from opencda_marl.core.traffic.sumo_adapter import Transform, Location + new_location = Location( + x=t.location.x, + y=t.location.y, + z=t.location.z + float(z) + ) + return Transform(location=new_location, rotation=t.rotation) + else: + # Real CARLA object + return carla.Transform(t.location + carla.Location(z=float(z)), t.rotation) def choose_same_lane(cands: List[carla.Waypoint], cur: carla.Waypoint) -> carla.Waypoint: diff --git a/opencda_marl/core/world_reset_manager.py b/opencda_marl/core/world_reset_manager.py new file mode 100644 index 0000000..c9bc6d1 --- /dev/null +++ b/opencda_marl/core/world_reset_manager.py @@ -0,0 +1,679 @@ +''' +Author : AXIBA leolihao@arizona.edu +Date : 2025-11-25 +FilePath : /OpenCDA-MARL/opencda_marl/core/world_reset_manager.py +Description : CARLA World Reset Manager for preventing training slowdown. + +Manages world reload to prevent server-side memory accumulation while +preserving all Python-side training state. + +Copyright (c) 2025 by AXIBA (leolihao@arizona.edu), All Rights Reserved. +''' +import gc +import time +import statistics +from collections import deque +from typing import Dict, Any, Optional, TYPE_CHECKING +from loguru import logger + +import carla + +if TYPE_CHECKING: + from opencda_marl.coordinator import MARLCoordinator + from opencda_marl.envs.marl_env import MARLEnv + + +class TrainingStateSnapshot: + """ + Captures all training state that must survive CARLA world reset. + + This includes replay buffers, model weights, optimizers, training + counters, and history deques - everything needed to continue + training seamlessly after world reload. + """ + + def __init__(self): + # Replay buffer (reference, not copy - avoid memory duplication) + self.replay_buffer = None + + # Model weights + self.model_state_dicts = {} + self.optimizer_state_dict = None + + # Training progress + self.epsilon = None + self.training_step = 0 + self.episode_count = 0 + + # History deques + self.reward_history = None + self.episode_length_history = None + self.success_rate_history = None + self.collision_rate_history = None + + # Convergence state + self.is_converged = False + self.convergence_episode = None + + # TensorBoard writer (keep reference for continuous logging) + self.tensorboard_writer = None + self.tensorboard_dir = None + self.tb_enabled = False + self.tb_log_frequency = 1 + self.tb_metrics = {} + + # Environment state + self.metrics = None + self.checkpoint_manager = None + self.training_config = None + + @classmethod + def capture(cls, marl_env: 'MARLEnv') -> 'TrainingStateSnapshot': + """ + Capture current training state before world reset. + + Args: + marl_env: The MARL environment containing training state + + Returns: + TrainingStateSnapshot with all state captured + """ + snapshot = cls() + + # Get algorithm from MARL manager + algorithm = marl_env.marl_manager.algorithm + + if algorithm is not None: + # Capture replay buffer reference (not a copy!) + # Works for DQN, TD3, and other buffer-based algorithms + if hasattr(algorithm, 'memory'): + snapshot.replay_buffer = algorithm.memory + logger.debug(f"Captured replay buffer with {len(snapshot.replay_buffer)} experiences") + + # Capture model weights (DQN has q_network/target_network) + if hasattr(algorithm, 'q_network'): + snapshot.model_state_dicts['q_network'] = \ + algorithm.q_network.state_dict() + snapshot.model_state_dicts['target_network'] = \ + algorithm.target_network.state_dict() + logger.debug("Captured DQN network weights") + + # Capture TD3 networks if present (has actor_target) + if hasattr(algorithm, 'actor') and hasattr(algorithm, 'actor_target'): + snapshot.model_state_dicts['actor'] = algorithm.actor.state_dict() + snapshot.model_state_dicts['actor_target'] = algorithm.actor_target.state_dict() + snapshot.model_state_dicts['critic'] = algorithm.critic.state_dict() + snapshot.model_state_dicts['critic_target'] = algorithm.critic_target.state_dict() + logger.debug("Captured TD3 network weights") + + # Capture MAPPO networks if present (no target networks) + elif hasattr(algorithm, 'actor') and hasattr(algorithm, 'critic'): + snapshot.model_state_dicts['actor'] = algorithm.actor.state_dict() + snapshot.model_state_dicts['critic'] = algorithm.critic.state_dict() + # MAPPO may have separate optimizers + if hasattr(algorithm, 'actor_optimizer'): + snapshot.model_state_dicts['actor_optimizer'] = algorithm.actor_optimizer.state_dict() + if hasattr(algorithm, 'critic_optimizer'): + snapshot.model_state_dicts['critic_optimizer'] = algorithm.critic_optimizer.state_dict() + # Capture rollout buffer if present (MAPPO on-policy buffer) + if hasattr(algorithm, 'rollout_buffer'): + snapshot.model_state_dicts['has_rollout_buffer'] = True + logger.debug("Captured MAPPO network weights") + + # Capture optimizer state + if hasattr(algorithm, 'optimizer'): + snapshot.optimizer_state_dict = algorithm.optimizer.state_dict() + + # Capture training progress (from BaseAlgorithm) + snapshot.epsilon = getattr(algorithm, 'epsilon', None) + snapshot.training_step = algorithm.training_step + snapshot.episode_count = algorithm.episode_count + + # Capture history deques (convert to lists for safe copying) + if hasattr(algorithm, 'reward_history'): + snapshot.reward_history = list(algorithm.reward_history) + if hasattr(algorithm, 'episode_length_history'): + snapshot.episode_length_history = list(algorithm.episode_length_history) + if hasattr(algorithm, 'success_rate_history'): + snapshot.success_rate_history = list(algorithm.success_rate_history) + if hasattr(algorithm, 'collision_rate_history'): + snapshot.collision_rate_history = list(algorithm.collision_rate_history) + + # Capture convergence state + snapshot.is_converged = algorithm.is_converged + snapshot.convergence_episode = algorithm.convergence_episode + + # Keep TensorBoard writer reference (file-based, survives reload) + snapshot.tensorboard_writer = algorithm.writer + snapshot.tensorboard_dir = getattr(algorithm, 'tensorboard_dir', None) + snapshot.tb_enabled = algorithm.tb_enabled + snapshot.tb_log_frequency = getattr(algorithm, 'tb_log_frequency', 1) + snapshot.tb_metrics = getattr(algorithm, 'tb_metrics', {}) + + # Capture environment state + snapshot.metrics = marl_env.metrics + snapshot.checkpoint_manager = marl_env.checkpoint_manager + snapshot.training_config = marl_env.training_config + + logger.info(f"Training state captured: episode={snapshot.episode_count}, " + f"training_step={snapshot.training_step}, " + f"epsilon={snapshot.epsilon}, " + f"replay_buffer_size={len(snapshot.replay_buffer) if snapshot.replay_buffer else 0}") + + return snapshot + + def restore(self, marl_env: 'MARLEnv'): + """ + Restore training state after world reset. + + Args: + marl_env: The MARL environment to restore state to + """ + algorithm = marl_env.marl_manager.algorithm + + if algorithm is not None: + # Restore replay buffer reference + if self.replay_buffer is not None and hasattr(algorithm, 'memory'): + algorithm.memory = self.replay_buffer + logger.debug(f"Restored replay buffer with {len(self.replay_buffer)} experiences") + + # Restore DQN model weights + if 'q_network' in self.model_state_dicts: + algorithm.q_network.load_state_dict( + self.model_state_dicts['q_network']) + algorithm.target_network.load_state_dict( + self.model_state_dicts['target_network']) + logger.debug("Restored DQN network weights") + + # Restore TD3 model weights (has target networks) + if 'actor_target' in self.model_state_dicts: + algorithm.actor.load_state_dict(self.model_state_dicts['actor']) + algorithm.actor_target.load_state_dict(self.model_state_dicts['actor_target']) + algorithm.critic.load_state_dict(self.model_state_dicts['critic']) + algorithm.critic_target.load_state_dict(self.model_state_dicts['critic_target']) + logger.debug("Restored TD3 network weights") + + # Restore MAPPO model weights (no target networks) + elif 'actor' in self.model_state_dicts and 'actor_target' not in self.model_state_dicts: + algorithm.actor.load_state_dict(self.model_state_dicts['actor']) + algorithm.critic.load_state_dict(self.model_state_dicts['critic']) + # Restore separate optimizers if present + if 'actor_optimizer' in self.model_state_dicts and hasattr(algorithm, 'actor_optimizer'): + algorithm.actor_optimizer.load_state_dict(self.model_state_dicts['actor_optimizer']) + if 'critic_optimizer' in self.model_state_dicts and hasattr(algorithm, 'critic_optimizer'): + algorithm.critic_optimizer.load_state_dict(self.model_state_dicts['critic_optimizer']) + logger.debug("Restored MAPPO network weights") + + # Restore optimizer state + if self.optimizer_state_dict and hasattr(algorithm, 'optimizer'): + algorithm.optimizer.load_state_dict(self.optimizer_state_dict) + + # Restore training progress + if self.epsilon is not None: + algorithm.epsilon = self.epsilon + algorithm.training_step = self.training_step + algorithm.episode_count = self.episode_count + + # Restore history deques (recreate with maxlen) + if self.reward_history is not None: + algorithm.reward_history = deque(self.reward_history, maxlen=100) + if self.episode_length_history is not None: + algorithm.episode_length_history = deque( + self.episode_length_history, maxlen=100) + if self.success_rate_history is not None: + algorithm.success_rate_history = deque( + self.success_rate_history, maxlen=100) + if self.collision_rate_history is not None: + algorithm.collision_rate_history = deque( + self.collision_rate_history, maxlen=100) + + # Restore convergence state + algorithm.is_converged = self.is_converged + algorithm.convergence_episode = self.convergence_episode + + # Restore TensorBoard writer + if self.tensorboard_writer: + algorithm.writer = self.tensorboard_writer + algorithm.tensorboard_dir = self.tensorboard_dir + algorithm.tb_enabled = self.tb_enabled + algorithm.tb_log_frequency = self.tb_log_frequency + algorithm.tb_metrics = self.tb_metrics + + # Restore environment state + marl_env.metrics = self.metrics + marl_env.checkpoint_manager = self.checkpoint_manager + marl_env.training_config = self.training_config + + logger.info(f"Training state restored: episode={self.episode_count}, " + f"training_step={self.training_step}, " + f"epsilon={self.epsilon}") + + +class WorldResetManager: + """ + Manages CARLA world reset/reload to prevent server-side memory accumulation. + + CARLA's server accumulates internal state (physics buffers, destroyed actor + references, collision detection caches) over time, causing simulation slowdown. + Python's gc.collect() cannot clean this - only client.reload_world() can. + + This manager: + 1. Tracks epoch count and triggers reset at configurable frequency + 2. Monitors step duration for automatic slowdown detection + 3. Orchestrates safe 7-phase world reload sequence + 4. Coordinates with MARLEnv to preserve all training state + + Key responsibilities: + - Performance monitoring (step time tracking, baseline establishment) + - Reset decision making (periodic + auto-reset on slowdown) + - Safe reload sequence execution + - Training state preservation across reloads + """ + + def __init__(self, config: Dict[str, Any], coordinator: 'MARLCoordinator'): + """ + Initialize WorldResetManager. + + Args: + config: world_reset configuration section + coordinator: Reference to MARLCoordinator for accessing components + """ + self.config = config + self.coordinator = coordinator + + # Reset frequency configuration + self.reset_frequency = config.get('reset_frequency', 15) + + # Auto-reset configuration + auto_cfg = config.get('auto_reset', {}) + self.auto_reset_enabled = auto_cfg.get('enabled', True) + self.slowdown_threshold = auto_cfg.get('slowdown_threshold', 1.5) + self.monitoring_window = auto_cfg.get('monitoring_window', 100) + self.min_epochs_before_auto = auto_cfg.get('min_epochs_before_auto', 3) + + # Performance monitoring state + self.baseline_step_time: Optional[float] = None + self.step_times: deque = deque(maxlen=200) + self.epochs_since_reset = 0 + + # Reset tracking + self.total_resets = 0 + self.last_reset_epoch = 0 + + # Logging configuration + self.log_reset_events = config.get('log_reset_events', True) + self.log_step_times = config.get('log_step_times', False) + + logger.info(f"WorldResetManager initialized: " + f"reset_frequency={self.reset_frequency}, " + f"auto_reset={self.auto_reset_enabled}, " + f"slowdown_threshold={self.slowdown_threshold}x") + + def record_step_time(self, step_duration: float): + """ + Record step duration for performance monitoring. + + Args: + step_duration: Time in seconds for the last step + """ + self.step_times.append(step_duration) + + if self.log_step_times: + logger.debug(f"Step time: {step_duration:.4f}s") + + # Establish baseline after collecting enough stable data + # Wait for 50 steps to avoid including initialization overhead + if self.baseline_step_time is None and len(self.step_times) >= 50: + self.baseline_step_time = statistics.median(self.step_times) + logger.info(f"Step time baseline established: {self.baseline_step_time:.4f}s") + + def should_auto_reset(self) -> bool: + """ + Check if auto-reset should be triggered based on performance degradation. + + Returns: + True if slowdown detected and auto-reset should trigger + """ + if not self.auto_reset_enabled: + return False + + if self.baseline_step_time is None: + return False + + if len(self.step_times) < self.monitoring_window: + return False + + if self.epochs_since_reset < self.min_epochs_before_auto: + return False + + # Calculate recent median step time + recent_times = list(self.step_times)[-self.monitoring_window:] + recent_median = statistics.median(recent_times) + slowdown_ratio = recent_median / self.baseline_step_time + + if slowdown_ratio > self.slowdown_threshold: + logger.warning(f"Performance degradation detected: " + f"{slowdown_ratio:.2f}x slower " + f"(baseline: {self.baseline_step_time:.4f}s, " + f"current: {recent_median:.4f}s)") + return True + + return False + + def on_episode_end(self): + """ + Called after each episode ends. Check if world reset is needed. + + This is the ONLY safe time to reset - never mid-episode. + """ + self.epochs_since_reset += 1 + + should_reset = False + reset_reason = "" + + # Check periodic reset + if (self.reset_frequency > 0 and + self.epochs_since_reset >= self.reset_frequency): + should_reset = True + reset_reason = f"Periodic reset (every {self.reset_frequency} epochs)" + + # Check auto-reset (only if periodic didn't trigger) + elif self.should_auto_reset(): + should_reset = True + reset_reason = "Auto-reset due to performance degradation" + + if should_reset: + if self.log_reset_events: + logger.warning(f"Triggering world reset: {reset_reason}") + + success = self.reload_world() + + if success: + logger.success(f"World reset #{self.total_resets} completed. " + f"Reason: {reset_reason}") + else: + logger.error(f"World reset FAILED. Training may continue to slow down.") + + def reload_world(self) -> bool: + """ + Safely reload CARLA world while preserving all training state. + + Executes a 7-phase reload sequence: + 1. Capture training state + 2. Cleanup CARLA resources + 3. Reload world (client.reload_world()) + 4. Reapply world settings + 5. Rebuild scenario components + 6. Restore training state + 7. Finalize and reset baseline + + MUST be called AFTER episode completes, NEVER mid-episode. + + Returns: + True if reload successful, False otherwise + """ + logger.warning(f"{'='*20} CARLA World Reset #{self.total_resets + 1} {'='*20}") + + try: + # Phase 1: Capture training state + logger.info("Phase 1/7: Capturing training state...") + state_snapshot = TrainingStateSnapshot.capture( + self.coordinator.marl_env) + + # Phase 2: Cleanup existing CARLA resources + logger.info("Phase 2/7: Cleaning up CARLA resources...") + self._cleanup_carla_resources() + + # Phase 3: Reload world + logger.info("Phase 3/7: Reloading CARLA world...") + success = self._reload_carla_world() + if not success: + logger.error("World reload failed!") + return False + + # Phase 4: Reapply world settings + logger.info("Phase 4/7: Reapplying world settings...") + self._apply_world_settings() + + # Phase 5: Rebuild scenario components + logger.info("Phase 5/7: Rebuilding scenario components...") + self._rebuild_scenario_components() + + # Phase 6: Restore training state + logger.info("Phase 6/7: Restoring training state...") + state_snapshot.restore(self.coordinator.marl_env) + + # Phase 7: Finalize reset + logger.info("Phase 7/7: Finalizing reset...") + self.total_resets += 1 + self.epochs_since_reset = 0 + self.last_reset_epoch = self.coordinator.states.get('episode', 0) + + # Reset performance baseline (will re-establish after 50 steps) + self.baseline_step_time = None + self.step_times.clear() + + # Force garbage collection + gc.collect() + + # Clear CUDA cache if available + try: + import torch + if torch.cuda.is_available(): + torch.cuda.empty_cache() + logger.debug("CUDA cache cleared") + except ImportError: + pass + + logger.success(f"World reset completed successfully. Total resets: {self.total_resets}") + return True + + except Exception as e: + logger.error(f"World reset failed with error: {e}") + import traceback + traceback.print_exc() + return False + + def _cleanup_carla_resources(self): + """ + Cleanup existing CARLA resources before reload. + + Each manager handles its own sensor cleanup - no pre-stopping needed. + IMPORTANT: world.tick() is called after each cleanup phase to ensure + CARLA server processes destroy commands and releases GPU memory. + """ + sm = self.coordinator.scenario_manager + world = self.coordinator.carla_world + + # Cleanup agent manager (destroys all vehicles and sensors) + if sm.agent_manager: + sm.agent_manager.cleanup() + logger.debug("Agent manager cleaned up") + + # Cleanup traffic manager + if sm.traffic_manager: + sm.traffic_manager.cleanup() + logger.debug("Traffic manager cleaned up") + + # Tick to process manager cleanups + try: + if world: + world.tick() + except Exception: + pass + + # Destroy any remaining actors in the world + # NOTE: We must re-fetch actors list after manager cleanups since + # the old list contains stale references to already-destroyed actors + try: + if world: + # Re-fetch fresh actor list after manager cleanups + actors = world.get_actors() + + # Destroy remaining sensors first (they depend on vehicles) + sensor_count = 0 + sensor_errors = 0 + for actor in actors.filter('sensor.*'): + try: + if actor.is_alive: + actor.destroy() + sensor_count += 1 + except RuntimeError: + # Actor already destroyed, ignore + sensor_errors += 1 + if sensor_count > 0: + logger.debug(f"Destroyed {sensor_count} remaining sensor actors") + if sensor_errors > 0: + logger.debug(f"Skipped {sensor_errors} already-destroyed sensors") + + # Tick after sensor cleanup + world.tick() + + # Re-fetch actors list again for vehicles + actors = world.get_actors() + + # Destroy remaining vehicles + vehicle_count = 0 + vehicle_errors = 0 + for actor in actors.filter('vehicle.*'): + try: + if actor.is_alive: + actor.destroy() + vehicle_count += 1 + except RuntimeError: + # Actor already destroyed, ignore + vehicle_errors += 1 + if vehicle_count > 0: + logger.debug(f"Destroyed {vehicle_count} remaining vehicle actors") + if vehicle_errors > 0: + logger.debug(f"Skipped {vehicle_errors} already-destroyed vehicles") + + # Final tick to process all destroys and release GPU memory + world.tick() + + except Exception as e: + logger.warning(f"Error during actor cleanup: {e}") + + def _reload_carla_world(self) -> bool: + """ + Reload world using client.reload_world(). + + This clears CARLA server-side memory and reloads the current map. + + Returns: + True if successful, False otherwise + """ + try: + client = self.coordinator.carla_client + + # reload_world() clears server memory and reloads current map + # This is the key call that fixes the slowdown! + client.reload_world() + + # Wait for world to be ready and re-get reference + for attempt in range(5): + try: + time.sleep(1.0) + self.coordinator.carla_world = client.get_world() + if self.coordinator.carla_world: + logger.info("World reference obtained successfully") + return True + except Exception as e: + logger.warning(f"Retry {attempt + 1}/5 getting world: {e}") + + return False + + except Exception as e: + logger.error(f"reload_world() failed: {e}") + return False + + def _apply_world_settings(self): + """Reapply world settings after reload.""" + world = self.coordinator.carla_world + sm = self.coordinator.scenario_manager + + # Apply synchronous mode settings from scenario manager + settings = world.get_settings() + settings.synchronous_mode = True # Always sync mode + settings.fixed_delta_seconds = sm.fixed_dt + world.apply_settings(settings) + + logger.debug(f"World settings reapplied: sync_mode=True, fixed_dt={sm.fixed_dt}") + + def _rebuild_scenario_components(self): + """Rebuild scenario manager components after world reload.""" + from opencda_marl.core.traffic import MARLTrafficManager + from opencda_marl.core import MARLAgentManager + from opencda_marl.envs import CarlaMonitor, CarlaSpectator + + sm = self.coordinator.scenario_manager + + # Update world reference in scenario manager + sm.world = self.coordinator.carla_world + sm.carla_map = sm.world.get_map() + + # Get config sections + traffic_cfg = sm.params.get("traffic", {}) + agents_cfg = self.coordinator.config.get("agents", {}) + + # Rebuild traffic manager with fresh world reference + sm.traffic_manager = MARLTrafficManager( + sm.world, + traffic_cfg, + sm.states, + sm.fixed_dt + ) + logger.debug("Traffic manager rebuilt") + + # Rebuild agent manager with fresh world reference + sm.agent_manager = MARLAgentManager( + agents_cfg, + sm.states, + sm.world, + sm.cav_world + ) + logger.debug("Agent manager rebuilt") + + # Update MARL environment references + self.coordinator.marl_env.world = sm.world + self.coordinator.marl_env.sm = sm + + # Rebuild CARLA monitor + self.coordinator.carla_monitor = CarlaMonitor( + sm.world, + marl_tm=sm.traffic_manager + ) + logger.debug("CARLA monitor rebuilt") + + # Update spectator + spectator_config = self.coordinator.config.get('spectator', {}) + self.coordinator.carla_spectator = CarlaSpectator( + sm.world, + spectator_config + ) + logger.debug("Spectator rebuilt") + + logger.info("All scenario components rebuilt successfully") + + def get_status(self) -> Dict[str, Any]: + """ + Get current status of the world reset manager. + + Returns: + Dictionary with status information + """ + status = { + 'total_resets': self.total_resets, + 'epochs_since_reset': self.epochs_since_reset, + 'reset_frequency': self.reset_frequency, + 'auto_reset_enabled': self.auto_reset_enabled, + 'baseline_step_time': self.baseline_step_time, + } + + if self.baseline_step_time and len(self.step_times) >= self.monitoring_window: + recent_times = list(self.step_times)[-self.monitoring_window:] + recent_median = statistics.median(recent_times) + status['current_step_time'] = recent_median + status['slowdown_ratio'] = recent_median / self.baseline_step_time + + return status diff --git a/opencda_marl/envs/__init__.py b/opencda_marl/envs/__init__.py index 99124ab..2005c60 100644 --- a/opencda_marl/envs/__init__.py +++ b/opencda_marl/envs/__init__.py @@ -16,7 +16,8 @@ from .carla_monitor import CarlaMonitor from .carla_spectator import CarlaSpectator from .marl_env import MARLEnv +from .sumo_marl_env import SumoMARLEnv from .evaluation import EvaluationManager from .evaluation_plots import EvaluationPlotter -__all__ = ['CarlaMonitor', 'CarlaSpectator', 'MARLEnv', 'EvaluationManager', 'EvaluationPlotter'] \ No newline at end of file +__all__ = ['CarlaMonitor', 'CarlaSpectator', 'MARLEnv', 'SumoMARLEnv', 'EvaluationManager', 'EvaluationPlotter'] \ No newline at end of file diff --git a/opencda_marl/envs/evaluation.py b/opencda_marl/envs/evaluation.py index 44854ef..ba66d2f 100644 --- a/opencda_marl/envs/evaluation.py +++ b/opencda_marl/envs/evaluation.py @@ -87,6 +87,8 @@ def _init_tracking(self): # Initialize reward tracking self.cumulative_reward = 0.0 self.agent_cumulative_rewards = {} + # Track when all vehicles complete for accurate throughput calculation + self.last_completion_step = None if not self.is_enabled(): logger.info("Evaluation disabled - no history tracking") @@ -165,6 +167,7 @@ def update_step(self, metrics: Dict[str, Any], rewards: Dict[int, float] = None) # Reset reward tracking for new episode self.cumulative_reward = 0.0 self.agent_cumulative_rewards.clear() + self.last_completion_step = None # Reset for next episode self._mem_management() self._data_management() @@ -183,17 +186,30 @@ def _step_statistics(self, metrics: Dict[str, Any], rewards: Dict[int, float] = success = metrics.get('success', 0) collision = metrics.get('collision', 0) active_agents = metrics.get('active_agents', 0) + pending_spawns = metrics.get('pending_spawns', 0) total_vehicles = success + collision + active_agents + # Track the step when ALL vehicles complete: + # - No pending spawns (all vehicles have been spawned) + # - No active agents (all spawned vehicles have finished) + if pending_spawns == 0 and active_agents == 0 and (success > 0 or collision > 0): + if self.last_completion_step is None: + self.last_completion_step = step + # calculate current rates + # Calculate rates including timeout vehicles for complete accountability success_rate = (success / total_vehicles * 100) if total_vehicles > 0 else 0 collision_rate = (collision / total_vehicles * 100) if total_vehicles > 0 else 0 + timeout_rate = (active_agents / total_vehicles * + 100) if total_vehicles > 0 else 0 # throughput: how many vehicles have completed per hour - if step > 0: - vps = success / step / self.fixed_dt + # Use completion step if all vehicles finished, otherwise current step + effective_step = self.last_completion_step if self.last_completion_step else step + if effective_step > 0: + vps = success / effective_step / self.fixed_dt throughput = vps * 3600 else: throughput = 0.0 @@ -226,7 +242,11 @@ def _step_statistics(self, metrics: Dict[str, Any], rewards: Dict[int, float] = 'total_vehicles': total_vehicles, 'success_rate': success_rate, 'collision_rate': collision_rate, + 'timeout_rate': timeout_rate, 'throughput': throughput, + # Episode length: step when ALL vehicles completed (for consistent reporting) + 'episode_length': effective_step, # Same as used in throughput calc + 'total_simulation_steps': step, # Total steps for reference 'step_reward': step_reward, 'cumulative_reward': self.cumulative_reward, 'avg_reward_per_agent': avg_reward_per_agent, @@ -425,9 +445,11 @@ def summary_evaluation(self): num_episodes = len(self.episode_history) success_rates = [ep['success_rate'] for ep in self.episode_history] collision_rates = [ep['collision_rate'] for ep in self.episode_history] + timeout_rates = [ep.get('timeout_rate', 0.0) for ep in self.episode_history] throughputs = [ep['throughput'] for ep in self.episode_history] total_successes = [ep['success'] for ep in self.episode_history] total_collisions = [ep['collision'] for ep in self.episode_history] + total_timeouts = [ep.get('active_agents', 0) for ep in self.episode_history] # Calculate comprehensive statistics final_stats = { @@ -442,15 +464,21 @@ def summary_evaluation(self): 'std_collision_rate': np.std(collision_rates), 'best_collision_rate': np.min(collision_rates), # Lower is better 'worst_collision_rate': np.max(collision_rates), + 'avg_timeout_rate': np.mean(timeout_rates), + 'std_timeout_rate': np.std(timeout_rates), + 'best_timeout_rate': np.min(timeout_rates), # Lower is better + 'worst_timeout_rate': np.max(timeout_rates), 'avg_throughput': np.mean(throughputs), 'std_throughput': np.std(throughputs), 'best_throughput': np.max(throughputs), 'worst_throughput': np.min(throughputs), 'total_success': sum(total_successes), 'total_collision': sum(total_collisions), - 'total_vehicles': sum(total_successes) + sum(total_collisions), + 'total_timeout': sum(total_timeouts), + 'total_vehicles': sum(total_successes) + sum(total_collisions) + sum(total_timeouts), 'final_episode_success_rate': success_rates[-1], 'final_episode_collision_rate': collision_rates[-1], + 'final_episode_timeout_rate': timeout_rates[-1], 'final_episode_throughput': throughputs[-1] } @@ -475,10 +503,13 @@ def summary_evaluation(self): f"(Best: {final_stats['best_success_rate']:.1f}%)") logger.info(f"Collision Rate: {final_stats['avg_collision_rate']:.1f}% ± {final_stats['std_collision_rate']:.1f}% " f"(Best: {final_stats['best_collision_rate']:.1f}%)") + logger.info(f"Timeout Rate: {final_stats['avg_timeout_rate']:.1f}% ± {final_stats['std_timeout_rate']:.1f}% " + f"(Best: {final_stats['best_timeout_rate']:.1f}%)") logger.info(f"Throughput: {final_stats['avg_throughput']:.1f} ± {final_stats['std_throughput']:.1f} vph " f"(Best: {final_stats['best_throughput']:.1f} vph)") logger.info(f"Total Vehicles: {final_stats['total_vehicles']} " - f"({final_stats['total_success']} success, {final_stats['total_collision']} collision)") + f"({final_stats['total_success']} success, {final_stats['total_collision']} collision, " + f"{final_stats['total_timeout']} timeout)") if num_episodes > 1: logger.info(f"Trends: Success {final_stats['success_rate_trend']:+.2f}%/ep, " @@ -488,11 +519,34 @@ def summary_evaluation(self): # Save summary to JSON file self._save_final_summary(final_stats) - + # Generate final comparison plot if we have multiple episodes if num_episodes > 1: self.plot_episode_comparison() - + + # Generate learning progress visualization with improvement metrics + # This is the paper-ready plot with tables and statistics + if num_episodes >= 100 and self.plotter: + logger.info("Generating learning progress analysis...") + self.plotter.plot_learning_progress(self.episode_history, window_size=100) + + # Generate and save improvement report + improvement_report = self.plotter.generate_improvement_report( + self.episode_history, window_size=100) + self._save_improvement_report(improvement_report) + + # Log key improvement metrics + if 'summary' in improvement_report: + summary = improvement_report['summary'] + logger.info(f"=== IMPROVEMENT SUMMARY ===") + logger.info(f"Success Rate: {summary['success_improvement_pct']:+.1f}% improvement") + logger.info(f"Collision Rate: {summary['collision_reduction_pct']:+.1f}% reduction") + logger.info(f"Throughput: {summary['throughput_improvement_pct']:+.1f}% improvement") + if summary.get('episodes_to_80pct_success'): + logger.info(f"Reached 80% success at episode {summary['episodes_to_80pct_success']}") + if summary.get('episodes_to_90pct_success'): + logger.info(f"Reached 90% success at episode {summary['episodes_to_90pct_success']}") + logger.info("=== EVALUATION COMPLETE ===") except Exception as e: @@ -500,11 +554,41 @@ def summary_evaluation(self): import traceback logger.debug(traceback.format_exc()) + def _save_improvement_report(self, report: dict): + """Save improvement report to JSON file""" + try: + # Add timestamp + report['generated_at'] = datetime.now().isoformat() + + # Convert numpy types to Python types for JSON serialization + def convert_numpy(obj): + if isinstance(obj, np.floating): + return float(obj) + elif isinstance(obj, np.integer): + return int(obj) + elif isinstance(obj, dict): + return {k: convert_numpy(v) for k, v in obj.items()} + elif isinstance(obj, list): + return [convert_numpy(item) for item in obj] + return obj + + report = convert_numpy(report) + + # Save to JSON + json_path = self.data_dir / "improvement_report.json" + with open(json_path, 'w') as f: + json.dump(report, f, indent=2) + + logger.info(f"Improvement report saved: {json_path}") + + except Exception as e: + logger.warning(f"Failed to save improvement report: {e}") + def _save_final_summary(self, summary_stats: dict): """Save final summary statistics to file""" try: - + # Add timestamp summary_stats['generated_at'] = datetime.now().isoformat() diff --git a/opencda_marl/envs/evaluation_plots.py b/opencda_marl/envs/evaluation_plots.py index 31c34a1..4377258 100644 --- a/opencda_marl/envs/evaluation_plots.py +++ b/opencda_marl/envs/evaluation_plots.py @@ -138,15 +138,17 @@ def plot_step_analysis(self, history: List[Dict], episode_num: int = 0) -> Path: if success and collision: ax4.plot(steps, success, color='green', linewidth=2, label='Cumulative Success') ax4.plot(steps, collision, color='red', linewidth=2, label='Cumulative Collision') - - # Add final statistics text box + + # Add final statistics text box - use total_vehicles to include timeouts final_success = success[-1] if success else 0 final_collision = collision[-1] if collision else 0 - final_total = final_success + final_collision + final_total = total_vehicles[-1] if total_vehicles else (final_success + final_collision) + final_timeout = final_total - final_success - final_collision final_success_pct = (final_success / final_total * 100) if final_total > 0 else 0 - - stats_text = f'Final Results:\n{final_success} successes ({final_success_pct:.1f}%)\n{final_collision} collisions' - ax4.text(0.02, 0.98, stats_text, transform=ax4.transAxes, + final_collision_pct = (final_collision / final_total * 100) if final_total > 0 else 0 + + stats_text = f'Final Results:\n{final_success} successes ({final_success_pct:.1f}%)\n{final_collision} collisions ({final_collision_pct:.1f}%)\n{final_timeout} timeouts' + ax4.text(0.02, 0.98, stats_text, transform=ax4.transAxes, verticalalignment='top', fontsize=10, bbox=dict(boxstyle='round', facecolor='lightblue', alpha=0.8)) @@ -257,15 +259,30 @@ def plot_episode_analysis(self, episode_data: Dict, episode_num: int = 0, fixed_ ax1.text(bar.get_x() + bar.get_width()/2., height + 0.1, f'{int(value)}', ha='center', va='bottom') - # Plot 2: Success vs Collision Rates + # Plot 2: Success vs Collision vs Timeout Rates ax2 = axes[0, 1] - rates = ['Success Rate', 'Collision Rate'] - rate_values = [episode_data['success_rate'], episode_data['collision_rate']] - rate_colors = ['green', 'red'] - - wedges, texts, autotexts = ax2.pie(rate_values, labels=rates, colors=rate_colors, - autopct='%1.1f%%', startangle=90) - ax2.set_title('Success vs Collision Rates') + rates = ['Success', 'Collision', 'Timeout'] + rate_values = [ + episode_data.get('success_rate', 0), + episode_data.get('collision_rate', 0), + episode_data.get('timeout_rate', 0) + ] + rate_colors = ['green', 'red', 'orange'] + + # Only include non-zero rates in pie chart + filtered_rates = [] + filtered_values = [] + filtered_colors = [] + for rate, value, color in zip(rates, rate_values, rate_colors): + if value > 0: + filtered_rates.append(rate) + filtered_values.append(value) + filtered_colors.append(color) + + if filtered_values: + wedges, texts, autotexts = ax2.pie(filtered_values, labels=filtered_rates, colors=filtered_colors, + autopct='%1.1f%%', startangle=90) + ax2.set_title('Vehicle Outcome Distribution') # Plot 3: Episode Throughput ax3 = axes[1, 0] @@ -514,10 +531,336 @@ def create_summary_plot(data: Dict[str, List], title: str, output_path: Path) -> ax4_twin.legend(loc='upper right') plt.tight_layout() - + # Save plot plt.savefig(output_path, dpi=150, bbox_inches='tight') plt.close() - + logger.info(f"Summary comparison plot saved: {output_path}") - return output_path \ No newline at end of file + return output_path + + def plot_learning_progress(self, episode_history: List[Dict], + window_size: int = 100) -> Path: + """ + Generate comprehensive learning progress visualization with improvement metrics. + + This is designed for paper-ready figures showing: + - Smoothed learning curves (moving averages) + - Performance improvement table + - Time-to-target statistics + - Relative improvement percentages + + Args: + episode_history: List of episode statistics dictionaries + window_size: Window size for moving averages (default 100) + + Returns: + Path to saved plot file + """ + if not episode_history or len(episode_history) < window_size: + logger.warning(f"Learning progress plot requires at least {window_size} episodes") + return None + + # Extract data + num_episodes = len(episode_history) + episodes = list(range(num_episodes)) + success_rates = [ep['success_rate'] for ep in episode_history] + collision_rates = [ep['collision_rate'] for ep in episode_history] + throughputs = [ep['throughput'] for ep in episode_history] + cumulative_rewards = [ep.get('cumulative_reward', 0) for ep in episode_history] + + # Calculate moving averages + def moving_average(data, window): + return [np.mean(data[max(0, i-window+1):i+1]) for i in range(len(data))] + + success_ma = moving_average(success_rates, window_size) + collision_ma = moving_average(collision_rates, window_size) + throughput_ma = moving_average(throughputs, window_size) + reward_ma = moving_average(cumulative_rewards, window_size) + + # Calculate improvement statistics + baseline_window = min(window_size, num_episodes // 4) # First quarter or window_size + final_window = min(window_size, num_episodes // 4) # Last quarter or window_size + + baseline_success = np.mean(success_rates[:baseline_window]) + final_success = np.mean(success_rates[-final_window:]) + baseline_collision = np.mean(collision_rates[:baseline_window]) + final_collision = np.mean(collision_rates[-final_window:]) + baseline_throughput = np.mean(throughputs[:baseline_window]) + final_throughput = np.mean(throughputs[-final_window:]) + baseline_reward = np.mean(cumulative_rewards[:baseline_window]) + final_reward = np.mean(cumulative_rewards[-final_window:]) + + # Calculate percentage improvements + def calc_improvement_pct(baseline, final): + if abs(baseline) < 0.01: + return 0.0 + return (final - baseline) / abs(baseline) * 100 + + success_improvement = calc_improvement_pct(baseline_success, final_success) + collision_reduction = calc_improvement_pct(baseline_collision, final_collision) * -1 # Negative is good + throughput_improvement = calc_improvement_pct(baseline_throughput, final_throughput) + reward_improvement = calc_improvement_pct(baseline_reward, final_reward) + + # Calculate time-to-target (episodes to reach X% success rate) + milestones = [50, 60, 70, 80, 90, 95] + time_to_target = {} + for target in milestones: + for i, rate in enumerate(success_rates): + if rate >= target: + time_to_target[target] = i + break + if target not in time_to_target: + time_to_target[target] = None # Never reached + + # Create figure with subplots and table + fig = plt.figure(figsize=(18, 14)) + gs = fig.add_gridspec(3, 3, height_ratios=[1, 1, 0.6], hspace=0.3, wspace=0.3) + + fig.suptitle(f'Learning Progress Analysis - {self.scenario_name} - {self.agent_name}\n' + f'({num_episodes} Episodes, {window_size}-Episode Moving Average)', + fontsize=14, fontweight='bold') + + # ============ Plot 1: Success Rate with Moving Average ============ + ax1 = fig.add_subplot(gs[0, 0]) + ax1.plot(episodes, success_rates, alpha=0.3, color='green', linewidth=0.5, label='Raw') + ax1.plot(episodes, success_ma, color='green', linewidth=2, label=f'MA-{window_size}') + ax1.axhline(y=baseline_success, color='gray', linestyle='--', alpha=0.5, label=f'Baseline: {baseline_success:.1f}%') + ax1.axhline(y=final_success, color='darkgreen', linestyle='--', alpha=0.7, label=f'Final: {final_success:.1f}%') + ax1.fill_between(episodes, success_ma, alpha=0.2, color='green') + ax1.set_xlabel('Episode') + ax1.set_ylabel('Success Rate (%)') + ax1.set_title(f'Success Rate (Δ{success_improvement:+.1f}%)') + ax1.legend(loc='lower right', fontsize=8) + ax1.grid(True, alpha=0.3) + ax1.set_ylim(0, 105) + + # ============ Plot 2: Collision Rate with Moving Average ============ + ax2 = fig.add_subplot(gs[0, 1]) + ax2.plot(episodes, collision_rates, alpha=0.3, color='red', linewidth=0.5, label='Raw') + ax2.plot(episodes, collision_ma, color='red', linewidth=2, label=f'MA-{window_size}') + ax2.axhline(y=baseline_collision, color='gray', linestyle='--', alpha=0.5, label=f'Baseline: {baseline_collision:.1f}%') + ax2.axhline(y=final_collision, color='darkred', linestyle='--', alpha=0.7, label=f'Final: {final_collision:.1f}%') + ax2.fill_between(episodes, collision_ma, alpha=0.2, color='red') + ax2.set_xlabel('Episode') + ax2.set_ylabel('Collision Rate (%)') + ax2.set_title(f'Collision Rate (Δ{-collision_reduction:+.1f}%)') + ax2.legend(loc='upper right', fontsize=8) + ax2.grid(True, alpha=0.3) + ax2.set_ylim(0, max(collision_rates) * 1.1 + 5) + + # ============ Plot 3: Throughput with Moving Average ============ + ax3 = fig.add_subplot(gs[0, 2]) + ax3.plot(episodes, throughputs, alpha=0.3, color='blue', linewidth=0.5, label='Raw') + ax3.plot(episodes, throughput_ma, color='blue', linewidth=2, label=f'MA-{window_size}') + ax3.axhline(y=baseline_throughput, color='gray', linestyle='--', alpha=0.5, label=f'Baseline: {baseline_throughput:.0f}') + ax3.axhline(y=final_throughput, color='darkblue', linestyle='--', alpha=0.7, label=f'Final: {final_throughput:.0f}') + ax3.fill_between(episodes, throughput_ma, alpha=0.2, color='blue') + ax3.set_xlabel('Episode') + ax3.set_ylabel('Throughput (vph)') + ax3.set_title(f'Throughput (Δ{throughput_improvement:+.1f}%)') + ax3.legend(loc='lower right', fontsize=8) + ax3.grid(True, alpha=0.3) + + # ============ Plot 4: Cumulative Reward with Moving Average ============ + ax4 = fig.add_subplot(gs[1, 0]) + ax4.plot(episodes, cumulative_rewards, alpha=0.3, color='purple', linewidth=0.5, label='Raw') + ax4.plot(episodes, reward_ma, color='purple', linewidth=2, label=f'MA-{window_size}') + ax4.axhline(y=baseline_reward, color='gray', linestyle='--', alpha=0.5) + ax4.axhline(y=final_reward, color='darkviolet', linestyle='--', alpha=0.7) + ax4.fill_between(episodes, reward_ma, alpha=0.2, color='purple') + ax4.set_xlabel('Episode') + ax4.set_ylabel('Cumulative Reward') + ax4.set_title(f'Episode Reward (Δ{reward_improvement:+.1f}%)') + ax4.legend(loc='lower right', fontsize=8) + ax4.grid(True, alpha=0.3) + + # ============ Plot 5: Success vs Collision Tradeoff ============ + ax5 = fig.add_subplot(gs[1, 1]) + # Color by episode (gradient from blue to red) + colors = plt.cm.coolwarm(np.linspace(0, 1, num_episodes)) + scatter = ax5.scatter(collision_rates, success_rates, c=episodes, cmap='coolwarm', + alpha=0.6, s=20, label='Episodes') + ax5.scatter([baseline_collision], [baseline_success], color='blue', s=200, + marker='s', edgecolors='black', linewidths=2, label='Start', zorder=5) + ax5.scatter([final_collision], [final_success], color='red', s=200, + marker='*', edgecolors='black', linewidths=2, label='End', zorder=5) + # Draw arrow from start to end + ax5.annotate('', xy=(final_collision, final_success), + xytext=(baseline_collision, baseline_success), + arrowprops=dict(arrowstyle='->', color='black', lw=2)) + ax5.set_xlabel('Collision Rate (%)') + ax5.set_ylabel('Success Rate (%)') + ax5.set_title('Learning Trajectory (Success vs Collision)') + ax5.legend(loc='best', fontsize=8) + ax5.grid(True, alpha=0.3) + cbar = plt.colorbar(scatter, ax=ax5) + cbar.set_label('Episode') + + # ============ Plot 6: Time-to-Target Bar Chart ============ + ax6 = fig.add_subplot(gs[1, 2]) + reached_targets = [(t, e) for t, e in time_to_target.items() if e is not None] + if reached_targets: + targets, episodes_to_reach = zip(*reached_targets) + bars = ax6.barh([f'{t}%' for t in targets], episodes_to_reach, color='teal', alpha=0.7) + ax6.set_xlabel('Episodes to Reach Target') + ax6.set_ylabel('Success Rate Target') + ax6.set_title('Time-to-Target Analysis') + # Add value labels + for bar, val in zip(bars, episodes_to_reach): + ax6.text(val + max(episodes_to_reach) * 0.02, bar.get_y() + bar.get_height()/2, + f'{val}', va='center', fontsize=9) + ax6.grid(True, alpha=0.3, axis='x') + else: + ax6.text(0.5, 0.5, 'No targets reached yet', transform=ax6.transAxes, + ha='center', va='center', fontsize=12) + ax6.set_title('Time-to-Target Analysis') + + # ============ Performance Improvement Table ============ + ax_table = fig.add_subplot(gs[2, :]) + ax_table.axis('off') + + # Create table data + table_data = [ + ['Metric', 'Baseline\n(First 100 ep)', 'Final\n(Last 100 ep)', 'Absolute Δ', 'Relative Δ', 'Status'], + ['Success Rate (%)', f'{baseline_success:.1f}', f'{final_success:.1f}', + f'{final_success - baseline_success:+.1f}', f'{success_improvement:+.1f}%', + '✓ Improved' if success_improvement > 0 else '✗ Declined'], + ['Collision Rate (%)', f'{baseline_collision:.1f}', f'{final_collision:.1f}', + f'{final_collision - baseline_collision:+.1f}', f'{-collision_reduction:+.1f}%', + '✓ Reduced' if collision_reduction > 0 else '✗ Increased'], + ['Throughput (vph)', f'{baseline_throughput:.0f}', f'{final_throughput:.0f}', + f'{final_throughput - baseline_throughput:+.0f}', f'{throughput_improvement:+.1f}%', + '✓ Improved' if throughput_improvement > 0 else '✗ Declined'], + ['Episode Reward', f'{baseline_reward:.0f}', f'{final_reward:.0f}', + f'{final_reward - baseline_reward:+.0f}', f'{reward_improvement:+.1f}%', + '✓ Improved' if reward_improvement > 0 else '✗ Declined'], + ] + + # Add time-to-target summary row + reached_str = ', '.join([f'{t}%@ep{e}' for t, e in time_to_target.items() if e is not None]) + if not reached_str: + reached_str = 'None reached' + table_data.append(['Time-to-Target', '-', '-', '-', '-', reached_str]) + + # Create and style table + table = ax_table.table(cellText=table_data[1:], colLabels=table_data[0], + loc='center', cellLoc='center', + colColours=['lightgray']*6) + table.auto_set_font_size(False) + table.set_fontsize(10) + table.scale(1.2, 1.8) + + # Color code the status column + for i in range(1, len(table_data)): + cell = table[(i-1, 5)] + if '✓' in table_data[i][5]: + cell.set_facecolor('#90EE90') # Light green + elif '✗' in table_data[i][5]: + cell.set_facecolor('#FFB6C1') # Light red + else: + cell.set_facecolor('#FFFACD') # Light yellow + + ax_table.set_title('Performance Improvement Summary', fontsize=12, fontweight='bold', pad=20) + + plt.tight_layout() + + # Save plot + plot_filename = f"learning_progress_{self.scenario_name}_{self.agent_name}.png" + plot_path = self.output_dir / plot_filename + + plt.savefig(plot_path, dpi=150, bbox_inches='tight') + plt.close() + + logger.info(f"Learning progress plot saved: {plot_path}") + return plot_path + + def generate_improvement_report(self, episode_history: List[Dict], + window_size: int = 100) -> Dict[str, Any]: + """ + Generate a comprehensive improvement statistics report. + + Returns a dictionary with all improvement metrics for paper/report use. + + Args: + episode_history: List of episode statistics dictionaries + window_size: Window size for baseline/final calculations + + Returns: + Dictionary with improvement statistics + """ + if not episode_history or len(episode_history) < 2: + return {'error': 'Insufficient data for improvement analysis'} + + num_episodes = len(episode_history) + + # Extract data + success_rates = [ep['success_rate'] for ep in episode_history] + collision_rates = [ep['collision_rate'] for ep in episode_history] + throughputs = [ep['throughput'] for ep in episode_history] + cumulative_rewards = [ep.get('cumulative_reward', 0) for ep in episode_history] + + # Calculate baseline and final windows + baseline_window = min(window_size, num_episodes // 4) + final_window = min(window_size, num_episodes // 4) + + def calc_stats(data, baseline_w, final_w): + baseline = data[:baseline_w] + final = data[-final_w:] + + baseline_mean = np.mean(baseline) + final_mean = np.mean(final) + + improvement_abs = final_mean - baseline_mean + improvement_pct = (improvement_abs / abs(baseline_mean) * 100) if abs(baseline_mean) > 0.01 else 0 + + return { + 'baseline_mean': baseline_mean, + 'baseline_std': np.std(baseline), + 'final_mean': final_mean, + 'final_std': np.std(final), + 'improvement_absolute': improvement_abs, + 'improvement_percent': improvement_pct, + 'min': min(data), + 'max': max(data), + 'overall_mean': np.mean(data), + 'overall_std': np.std(data), + } + + # Time-to-target analysis + milestones = [50, 60, 70, 80, 90, 95] + time_to_target = {} + for target in milestones: + for i, rate in enumerate(success_rates): + if rate >= target: + time_to_target[target] = i + break + if target not in time_to_target: + time_to_target[target] = None + + report = { + 'num_episodes': num_episodes, + 'window_size': window_size, + 'success_rate': calc_stats(success_rates, baseline_window, final_window), + 'collision_rate': calc_stats(collision_rates, baseline_window, final_window), + 'throughput': calc_stats(throughputs, baseline_window, final_window), + 'cumulative_reward': calc_stats(cumulative_rewards, baseline_window, final_window), + 'time_to_target': time_to_target, + 'scenario_name': self.scenario_name, + 'agent_name': self.agent_name, + } + + # Add summary statistics + report['summary'] = { + 'success_improvement_pct': report['success_rate']['improvement_percent'], + 'collision_reduction_pct': -report['collision_rate']['improvement_percent'], + 'throughput_improvement_pct': report['throughput']['improvement_percent'], + 'reward_improvement_pct': report['cumulative_reward']['improvement_percent'], + 'best_success_rate': max(success_rates), + 'best_throughput': max(throughputs), + 'lowest_collision_rate': min(collision_rates), + 'episodes_to_80pct_success': time_to_target.get(80), + 'episodes_to_90pct_success': time_to_target.get(90), + } + + return report \ No newline at end of file diff --git a/opencda_marl/envs/marl_env.py b/opencda_marl/envs/marl_env.py index 7f9823f..120dd94 100644 --- a/opencda_marl/envs/marl_env.py +++ b/opencda_marl/envs/marl_env.py @@ -47,15 +47,27 @@ def __init__(self, scenario_manager: MARLScenarioManager, config: Dict = {}): self.terminal_agents = set() # Track agents that received terminal rewards self.current_step_rewards = {} # Store current step rewards for evaluation + # Near-miss tracking (TTC < threshold without collision) + # Shows safety learning: decreasing near-misses = learning to avoid danger + self.near_miss_count = 0 # Count per episode + self.near_miss_agents = set() # Track agents with near-miss this step (prevent double counting) + + # TTC violation tracking (TTC < safe_threshold) + # Tracks % of agent-steps with dangerous TTC for paper metrics + self.ttc_violation_count = 0 # Count of TTC violations per episode + self.ttc_check_count = 0 # Total TTC checks per episode + # Reward parameters self.reward_params = self._default_reward_params() self.reward_params.update(self.config.get('rewards', {})) - # Training metrics - self.metrics = Metrics() - # Training configuration self.training_config = self.config.get('training', {}) + + # Training metrics (export history every N episodes) + metrics_export_interval = self.training_config.get('metrics_export_interval', 10) + metrics_export_dir = self.training_config.get('metrics_export_dir', 'metrics_history') + self.metrics = Metrics(export_interval=metrics_export_interval, export_dir=metrics_export_dir) self.is_training_mode = self.training_config.get('training_mode', True) # Override training mode for baseline agents @@ -63,19 +75,20 @@ def __init__(self, scenario_manager: MARLScenarioManager, config: Dict = {}): self.is_training_mode = False logger.info(f"Baseline agent '{agent_type}' detected - disabling training mode") - # Initialize checkpoint manager if training - if self.is_training_mode: - checkpoint_dir = self.training_config.get( - 'checkpoint_dir', f'checkpoints/{algorithm}') - self.checkpoint_manager = CheckpointManager( - checkpoint_dir, algorithm) - - # Load checkpoint if specified - load_checkpoint = self.training_config.get('load_checkpoint') - if load_checkpoint: - self._load_checkpoint_from_config(load_checkpoint) - else: - self.checkpoint_manager = None + # Initialize checkpoint manager + # Note: We need checkpoint manager even in evaluation mode to load trained weights + checkpoint_dir = self.training_config.get( + 'checkpoint_dir', f'checkpoints/{algorithm}') + self.checkpoint_manager = CheckpointManager( + checkpoint_dir, algorithm) if self.is_training_mode else None + + # Load checkpoint if specified - works in BOTH training and evaluation modes + # In evaluation mode (training_mode: false), this loads trained weights to test performance + load_checkpoint = self.training_config.get('load_checkpoint') + if load_checkpoint: + self._load_checkpoint_from_config(load_checkpoint) + if not self.is_training_mode: + logger.info(f"Evaluation mode: Loaded checkpoint '{load_checkpoint}' for testing") def step(self): # Get current observations from scenario manager @@ -88,28 +101,37 @@ def step(self): # Execute scenario manager step with target speeds out = self.sm.step(target_speeds) - # Calculate rewards from events - self.events = out.get('event', []) - rewards = self._calculate_rewards(self.events) - - # Get new observations for MARL learning + # Get new observations for reward calculation (includes min_ttc, distance_to_destination) next_observations = self.sm.get_observations() - # Update MARL algorithm with experience (only if training) - if self.is_training_mode and self.previous_observations: + # Calculate rewards from events AND observations (Phase 3: includes TTC and progress rewards) + self.events = out.get('event', []) + rewards = self._calculate_rewards(self.events, observations=next_observations) + + # Update MARL algorithm with CURRENT step's transition + # Transition: (observations, action, reward, next_observations) + # - observations: state before action (S_t) + # - action: stored in last_actions during compute_actions + # - reward: calculated for taking action from observations + # - next_observations: state after action (S_t+1) + if self.is_training_mode: self.marl_manager.update( - rewards, self.previous_observations, next_observations) + rewards, observations, next_observations) # Use current observations! - # Store observations for next update - self.previous_observations = observations.copy() + # Store current observations as previous for next step's progress calculation + self.previous_observations = next_observations.copy() # Store current step rewards for evaluation self.current_step_rewards = rewards.copy() self.episode_events.extend(self.events) - # Update training metrics - self.metrics.update_step(rewards) + # Count successes this step for accurate episode_length tracking + step_successes = sum(1 for e in self.events if e.event_type == "success") + + # Update training metrics with observations and RL-commanded target speeds + # Pass target_speeds directly so we track what RL commanded (not adapter cached values) + self.metrics.update_step(rewards, next_observations, target_speeds, step_successes) # --------------------------------------------------------------------- # # Public Methods @@ -233,12 +255,17 @@ def _load_checkpoint_from_config(self, checkpoint_path: str): # Private Methods # --------------------------------------------------------------------- # - def _calculate_rewards(self, events: List[StepEvent]) -> Dict[str, float]: + def _calculate_rewards(self, events: List[StepEvent], observations: Dict = None) -> Dict[str, float]: """ Calculate rewards for all agents based on events and current state. + Includes Phase 3 enhancements: + - TTC-based safety reward (proactive penalty for dangerous proximity) + - Progress-toward-goal reward (dense feedback for movement toward destination) + Args: events: List of event strings from scenario manager + observations: Current observations dict (optional, for TTC/progress rewards) Returns: Dict mapping agent_id to reward value @@ -253,6 +280,10 @@ def _calculate_rewards(self, events: List[StepEvent]) -> Dict[str, float]: speed_bonus = self.reward_params.get("speed_bonus", 0.0) speed_threshold = self.reward_params.get("speed_threshold", 40.0) # km/h + # Get stop penalty parameters (Phase 3.1) + stop_threshold = self.reward_params.get("stop_threshold", 5.0) # km/h + stop_penalty = self.reward_params.get("stop_penalty", -3.0) + try: # Get current agents from scenario manager agents = self.sm.agents @@ -261,21 +292,77 @@ def _calculate_rewards(self, events: List[StepEvent]) -> Dict[str, float]: for agent in agents: agent_id = agent.actor_id base_reward = step_penalty - - # Add speed bonus if agent is going fast enough - if speed_bonus > 0: - try: - # Get agent's current speed in km/h - velocity = agent.vehicle.get_velocity() - speed_kmh = 3.6 * math.sqrt(velocity.x**2 + velocity.y**2 + velocity.z**2) - - if speed_kmh > speed_threshold: - base_reward += speed_bonus - logger.debug(f"Speed bonus applied to agent {agent_id}: {speed_kmh:.1f} km/h > {speed_threshold} km/h") - - except Exception as e: - logger.debug(f"Could not get speed for agent {agent_id}: {e}") - + + # Get agent's current speed in km/h + speed_kmh = 0.0 + try: + velocity = agent.vehicle.get_velocity() + speed_kmh = 3.6 * math.sqrt(velocity.x**2 + velocity.y**2 + velocity.z**2) + + # Add speed bonus if agent is going fast enough + if speed_bonus > 0 and speed_kmh > speed_threshold: + base_reward += speed_bonus + logger.debug(f"Speed bonus applied to agent {agent_id}: {speed_kmh:.1f} km/h > {speed_threshold} km/h") + + # Add stop penalty if agent has nearly stopped (Phase 3.1) + # Encourages gradual deceleration over hard stops + if speed_kmh < stop_threshold: + base_reward += stop_penalty + logger.debug(f"Stop penalty applied to agent {agent_id}: {speed_kmh:.1f} km/h < {stop_threshold} km/h") + + except Exception as e: + logger.debug(f"Could not get speed for agent {agent_id}: {e}") + + # Phase 3: TTC-based safety reward - DISABLED + # TTC penalty was causing overly conservative behavior + # Collision penalty (-500) is sufficient safety signal + # Keep TTC tracking for metrics only + if observations and agent_id in observations: + obs = observations[agent_id] + min_ttc = obs.get('min_ttc', float('inf')) + + # Track TTC metrics (for paper/analysis) without affecting reward + self._track_ttc_metrics(min_ttc, agent_id) + + # Clearance speed bonus: reward faster speeds when path is clear + # This teaches: "go fast when safe, collision penalty teaches when to slow" + clearance_speed_bonus = self.reward_params.get('clearance_speed_bonus', 0.3) + clearance_threshold = self.reward_params.get('clearance_threshold', 30.0) + dist_to_front = obs.get('distance_to_front_vehicle', float('inf')) + + if dist_to_front > clearance_threshold or dist_to_front == float('inf'): + if speed_kmh > 20.0: # Above minimum useful speed + speed_ratio = min(speed_kmh / 65.0, 1.0) + clearance_bonus = clearance_speed_bonus * speed_ratio + base_reward += clearance_bonus + logger.debug(f"Clearance bonus to agent {agent_id}: speed={speed_kmh:.1f}, dist_front={dist_to_front:.1f}m, bonus={clearance_bonus:.3f}") + + # Yielding bonus: reward for slowing down when nearby vehicle has low TTC + # This encourages cooperative behavior - yield to let others pass safely + yielding_bonus = self.reward_params.get('yielding_bonus', 0.0) + if yielding_bonus > 0 and agent_id in self.previous_observations: + yielding_ttc_threshold = self.reward_params.get('yielding_ttc_threshold', 3.0) + yielding_speed_drop = self.reward_params.get('yielding_speed_drop', 5.0) + + # Check if there's a nearby vehicle with low TTC + if min_ttc < yielding_ttc_threshold and min_ttc != float('inf'): + # Check if ego vehicle is slowing down (yielding behavior) + prev_speed = self.previous_observations[agent_id].get('speed', 0.0) + if prev_speed - speed_kmh >= yielding_speed_drop: + base_reward += yielding_bonus + logger.debug(f"Yielding bonus to agent {agent_id}: slowed {prev_speed:.1f} -> {speed_kmh:.1f} km/h, TTC={min_ttc:.2f}s") + + # Phase 3: Add progress reward (requires previous observations) + if agent_id in self.previous_observations: + prev_obs = self.previous_observations[agent_id] + progress_reward = self._calculate_progress_reward( + current_dist_to_dest=obs.get('distance_to_destination', 999.0), + prev_dist_to_dest=prev_obs.get('distance_to_destination', 999.0), + current_dist_to_int=obs.get('distance_to_intersection', 100.0), + prev_dist_to_int=prev_obs.get('distance_to_intersection', 100.0) + ) + base_reward += progress_reward + rewards[agent_id] = base_reward # Process events for rewards (prevent duplicate terminal rewards) @@ -297,7 +384,7 @@ def _calculate_rewards(self, events: List[StepEvent]) -> Dict[str, float]: except Exception: pass # Use base reward if speed calculation fails rewards[agent_id] = base_reward - + if event.event_type == "collision" and agent_id not in self.terminal_agents: rewards[agent_id] = collision_reward self.terminal_agents.add(agent_id) @@ -321,9 +408,138 @@ def _default_reward_params(self): "success": 120.0, "step_penalty": -0.5, "speed_bonus": 0.0, # Bonus for maintaining speed above threshold - "speed_threshold": 40.0 # km/h threshold for speed bonus + "speed_threshold": 40.0, # km/h threshold for speed bonus + # TTC-based safety reward parameters (Phase 3) + "ttc_safe_threshold": 3.0, # seconds - no penalty above this + "ttc_caution_threshold": 2.0, # seconds - caution zone + "ttc_danger_threshold": 1.0, # seconds - danger zone + "ttc_caution_penalty": -0.5, # penalty in caution zone + "ttc_danger_penalty": -2.0, # penalty in danger zone + "ttc_critical_penalty": -5.0, # penalty in critical zone (< 1s) + # Progress reward parameters (Phase 3) + "progress_scale": 0.5, # scale factor for progress reward + "junction_threshold": 5.0, # meters - threshold for "in junction" + # Stop penalty parameters (Phase 3.1) + "stop_threshold": 5.0, # km/h - below this speed is considered "stopped" + "stop_penalty": -3.0 # penalty for stopping (encourages gradual deceleration) } + # --------------------------------------------------------------------- # + # TTC and Progress Reward Methods (Phase 3 Enhancement) + # --------------------------------------------------------------------- # + + def _calculate_ttc_reward(self, min_ttc: float, agent_id: int = None) -> float: + """ + Calculate TTC-based safety reward using smooth exponential penalty. + + Uses smooth exponential function instead of hard thresholds for better + gradient-based learning. Penalty increases smoothly as TTC decreases. + + Formula: penalty = -max_penalty * exp(-decay_rate * ttc) + + Args: + min_ttc: Minimum time-to-collision across all nearby vehicles (seconds) + agent_id: Agent ID for near-miss tracking (optional) + + Returns: + float: TTC reward (0.0 for safe, negative for dangerous situations) + """ + safe_threshold = self.reward_params.get('ttc_safe_threshold', 4.0) + max_penalty = self.reward_params.get('ttc_max_penalty', 10.0) + decay_rate = self.reward_params.get('ttc_decay_rate', 1.5) + near_miss_threshold = self.reward_params.get('ttc_near_miss_threshold', 2.0) + + # Track TTC checks for violation rate calculation + self.ttc_check_count += 1 + + # Safe - no penalty (above threshold or no nearby vehicles) + if min_ttc > safe_threshold or min_ttc == float('inf'): + return 0.0 + + # Track any TTC below safe threshold as a violation (for paper metrics) + self.ttc_violation_count += 1 + + # Smooth exponential penalty: increases as TTC decreases + # At TTC=4.0s: penalty ≈ -0.02 (near zero) + # At TTC=2.0s: penalty ≈ -0.5 + # At TTC=1.0s: penalty ≈ -2.2 + # At TTC=0.5s: penalty ≈ -4.7 + # At TTC=0.0s: penalty = -10.0 + penalty = -max_penalty * math.exp(-decay_rate * min_ttc) + + # Near-miss tracking (for metrics) - count when TTC drops below threshold + if min_ttc < near_miss_threshold and agent_id is not None: + if agent_id not in self.near_miss_agents: + self.near_miss_count += 1 + self.near_miss_agents.add(agent_id) + logger.debug(f"Near-miss: agent {agent_id}, TTC={min_ttc:.2f}s, penalty={penalty:.2f}") + + return penalty + + def _track_ttc_metrics(self, min_ttc: float, agent_id: int = None) -> None: + """ + Track TTC metrics without affecting reward (for paper/analysis). + + This is called after TTC reward was disabled to maintain metric tracking + for violation rates and near-miss counts. + + Args: + min_ttc: Minimum time-to-collision across all nearby vehicles (seconds) + agent_id: Agent ID for near-miss tracking (optional) + """ + safe_threshold = self.reward_params.get('ttc_safe_threshold', 4.0) + near_miss_threshold = self.reward_params.get('ttc_near_miss_threshold', 2.0) + + # Track TTC checks for violation rate calculation + self.ttc_check_count += 1 + + # Track any TTC below safe threshold as a violation (for paper metrics) + if min_ttc < safe_threshold and min_ttc != float('inf'): + self.ttc_violation_count += 1 + + # Near-miss tracking (for metrics) + if min_ttc < near_miss_threshold and agent_id is not None: + if agent_id not in self.near_miss_agents: + self.near_miss_count += 1 + self.near_miss_agents.add(agent_id) + logger.debug(f"Near-miss tracked: agent {agent_id}, TTC={min_ttc:.2f}s") + + def _calculate_progress_reward(self, current_dist_to_dest: float, prev_dist_to_dest: float, + current_dist_to_int: float, prev_dist_to_int: float) -> float: + """ + Calculate progress-toward-goal reward. + + Two-phase approach: + 1. Before intersection: reward reducing distance to intersection + 2. In/past junction: reward reducing distance to destination + + Args: + current_dist_to_dest: Current distance to destination (meters) + prev_dist_to_dest: Previous distance to destination (meters) + current_dist_to_int: Current distance to intersection (meters) + prev_dist_to_int: Previous distance to intersection (meters) + + Returns: + float: Progress reward (positive for progress, negative for regression) + """ + junction_threshold = self.reward_params.get('junction_threshold', 5.0) + in_junction = current_dist_to_int < junction_threshold + + if not in_junction: + # Approaching intersection phase + progress = prev_dist_to_int - current_dist_to_int + else: + # In/past junction: use destination distance + progress = prev_dist_to_dest - current_dist_to_dest + + # Normalize: typical step progress ~2-4m at 40-80 km/h + # Cap at ±5m of progress per step + import numpy as np + progress_reward = np.clip(progress / 5.0, -1.0, 1.0) + scale = self.reward_params.get('progress_scale', 0.5) + + return progress_reward * scale + # --------------------------------------------------------------------- # # Clean Up # --------------------------------------------------------------------- # @@ -334,6 +550,12 @@ def reset_episode(self): # Create episode-specific state snapshot BEFORE any resets # This captures the actual current episode stats current_episode = self.sm.states.get('episode', 0) + + # Calculate TTC violation rate (% of TTC checks that were violations) + ttc_violation_rate = 0.0 + if self.ttc_check_count > 0: + ttc_violation_rate = (self.ttc_violation_count / self.ttc_check_count) * 100.0 + episode_states = { 'max_steps': self.sm.states['max_steps'], 'max_episodes': self.sm.states['max_episodes'], @@ -342,7 +564,11 @@ def reset_episode(self): 'collision': self.sm.states['collision'], # Current episode stats 'success': self.sm.states['success'], # Current episode stats 'active_agents': self.sm.states['active_agents'], - 'fixed_dt': self.sm.states.get('fixed_dt', 0.05) + 'fixed_dt': self.sm.states.get('fixed_dt', 0.05), + # Near-miss tracking for learning analysis + 'near_miss_count': self.near_miss_count, + # TTC violation rate for paper metrics (% of checks with TTC < safe threshold) + 'ttc_violation_rate': ttc_violation_rate } # Finish current episode metrics with clean episode-specific snapshot @@ -352,8 +578,8 @@ def reset_episode(self): if self.is_training_mode: self._handle_training_episode_end(episode_metrics, current_episode) - # Reset MARL algorithm - self.marl_manager.reset_episode() + # Reset MARL algorithm and log episode metrics to TensorBoard + self.marl_manager.reset_episode(episode_metrics=episode_metrics) # Reset environment state self.events = None @@ -362,5 +588,13 @@ def reset_episode(self): self.terminal_agents = set() self.current_step_rewards.clear() + # Reset near-miss tracking for new episode + self.near_miss_count = 0 + self.near_miss_agents.clear() + + # Reset TTC violation tracking for new episode + self.ttc_violation_count = 0 + self.ttc_check_count = 0 + logger.info(f"Episode {current_episode} reset completed") return episode_metrics diff --git a/opencda_marl/envs/sumo_marl_env.py b/opencda_marl/envs/sumo_marl_env.py new file mode 100644 index 0000000..f50be78 --- /dev/null +++ b/opencda_marl/envs/sumo_marl_env.py @@ -0,0 +1,643 @@ +''' +Author : AXIBA leolihao@arizona.edu +Date : 2025-11-16 +FilePath : /OpenCDA-MARL/opencda_marl/envs/sumo_marl_env.py +Description : SUMO-only MARL environment for fast training +Copyright (c) 2025 by AXIBA (leolihao@arizona.edu), All Rights Reserved. +''' +import math +import os +import sys +from typing import Dict, List, Any, Tuple +from loguru import logger +import numpy as np + +# Check SUMO_HOME +if 'SUMO_HOME' in os.environ: + sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools')) +else: + sys.exit("please declare environment variable 'SUMO_HOME'") + +import traci # pylint: disable=import-error +import sumolib # pylint: disable=import-error + +from opencda_marl.core.events import StepEvent +from opencda_marl.core.marl import MARLManager +from opencda_marl.core.marl.metrics import TrainingMetrics as Metrics +from opencda_marl.core.marl.checkpoint import CheckpointManager +from opencda_marl.core.traffic.sumo_adapter import SumoWorld, SumoMARLPlanner +from opencda_marl.core.traffic.traffic_manager import MARLTrafficManager +from opencda_marl.core.traffic.sumo_spawner import SumoVehicleSpawner + + +class SumoMARLEnv: + """ + SUMO-only MARL environment for fast training. + + Uses TraCI to control SUMO vehicles directly without CARLA rendering. + Provides same observation/action space as CARLA-based MARLEnv for transfer learning. + """ + + def __init__(self, config: Dict): + """ + Initialize SUMO MARL environment. + + Args: + config: Full configuration dictionary including: + - meta.sumo_cfg: Path to SUMO configuration file + - world.fixed_delta_seconds: Simulation step length + - scenario.simulation: Episode parameters + - MARL: Algorithm configuration + - rewards: Reward parameters + """ + self.config = config + + # Extract configuration sections + self.meta_config = config.get('meta', {}) + self.world_config = config.get('world', {}) + self.scenario_config = config.get('scenario', {}) + self.simulation_config = self.scenario_config.get('simulation', {}) + self.marl_config = config.get('MARL', {}) + self.training_config = self.marl_config.get('training', {}) + + # SUMO connection parameters + self.sumo_cfg = self.meta_config.get('sumo_cfg') + if not self.sumo_cfg or not os.path.isfile(self.sumo_cfg): + raise ValueError(f"SUMO configuration file not found: {self.sumo_cfg}") + + self.step_length = self.world_config.get('fixed_delta_seconds', 0.05) + self.sumo_port = self.world_config.get('sumo_port', 8873) + self.use_gui = self.world_config.get('sumo_gui', False) + + # Episode parameters + self.max_steps = self.simulation_config.get('max_steps', 2400) + self.max_episodes = self.simulation_config.get('max_episodes', 1000) + + # State tracking + self.current_step = 0 + self.current_episode = 0 + self.total_steps = 0 + + # Vehicle tracking + self.active_vehicles = {} # {veh_id: vehicle_info} + self.departed_vehicles = set() # Vehicles that have arrived + self.collided_vehicles = set() # Vehicles involved in collisions + + # Intersection center (will be calculated from network) + self.intersection_center = None + + # Traffic Manager (will be initialized after SUMO starts) + self.traffic_manager = None + self.vehicle_spawner = None + self.use_traffic_manager = self.scenario_config.get('traffic', {}).get('mode') in ['live', 'record', 'replay'] + + # MARL Manager + algorithm = self.marl_config.get('algorithm', 'td3') + self.marl_manager = MARLManager(self.marl_config, algorithm) + + # Reward parameters + self.reward_params = self._default_reward_params() + self.reward_params.update(self.marl_config.get('rewards', {})) + + # Episode tracking + self.episode_events = [] + self.previous_observations = {} + self.terminal_agents = set() + self.current_step_rewards = {} + + # Training metrics (export history every N episodes) + metrics_export_interval = self.training_config.get('metrics_export_interval', 10) + metrics_export_dir = self.training_config.get('metrics_export_dir', 'metrics_history') + self.metrics = Metrics(export_interval=metrics_export_interval, export_dir=metrics_export_dir) + + # Training mode + self.is_training_mode = self.training_config.get('training_mode', True) + + # Checkpoint manager + if self.is_training_mode: + checkpoint_dir = self.training_config.get( + 'checkpoint_dir', f'checkpoints/sumo_{algorithm}') + self.checkpoint_manager = CheckpointManager( + checkpoint_dir, algorithm) + + # Load checkpoint if specified + load_checkpoint = self.training_config.get('load_checkpoint') + if load_checkpoint: + self._load_checkpoint_from_config(load_checkpoint) + else: + self.checkpoint_manager = None + + # Initialize SUMO + self._start_sumo() + + def _start_sumo(self): + """Start SUMO simulation via TraCI.""" + logger.info(f"Starting SUMO with config: {self.sumo_cfg}") + + # Choose SUMO binary + if self.use_gui: + sumo_binary = sumolib.checkBinary('sumo-gui') + else: + sumo_binary = sumolib.checkBinary('sumo') + + # Start TraCI + sumo_cmd = [ + sumo_binary, + '--step-length', str(self.step_length), + '--collision.action', 'teleport', # Remove vehicles on collision (realistic for transfer learning) + '--collision.check-junctions', 'true', + '--collision.mingap-factor', '1.0', # Minimum gap for collision detection + '--no-warnings', 'false', + '--quit-on-end', 'false', # Don't auto-quit + ] + + # If using traffic manager, only load network file (no routes) + if self.use_traffic_manager: + # Extract network file from sumocfg + net_file = self._get_network_file_from_config() + sumo_cmd.extend(['--net-file', net_file]) + logger.info("Using MARLTrafficManager - loading network only (no route file)") + else: + # Use full config (includes routes) + sumo_cmd.extend(['--configuration-file', self.sumo_cfg]) + logger.info("Using static route file from sumocfg") + + # Add auto-start flag for GUI to prevent manual start button + if self.use_gui: + sumo_cmd.append('--start') + + traci.start(sumo_cmd, port=self.sumo_port) + + # Calculate intersection center from network + self._calculate_intersection_center() + + # Initialize traffic manager if using dynamic traffic + if self.use_traffic_manager: + self._initialize_traffic_manager() + + logger.info("SUMO started successfully") + + def _get_network_file_from_config(self) -> str: + """Extract network file path from SUMO configuration file.""" + import xml.etree.ElementTree as ET + from pathlib import Path + + # Parse sumocfg file + tree = ET.parse(self.sumo_cfg) + root = tree.getroot() + + # Find net-file element + net_elem = root.find('.//net-file') + if net_elem is None: + raise ValueError(f"No net-file found in {self.sumo_cfg}") + + net_file = net_elem.get('value') + + # Resolve relative path + cfg_dir = Path(self.sumo_cfg).parent + net_path = cfg_dir / net_file + + return str(net_path) + + def _calculate_intersection_center(self): + """Calculate the center of the intersection from SUMO network.""" + # Get all junctions + junctions = traci.junction.getIDList() + + if not junctions: + logger.warning("No junctions found in SUMO network") + self.intersection_center = (0.0, 0.0) + return + + # Find junction 4 (main intersection in our network) + # This ensures coordinate consistency with CARLA + main_junction = '4' if '4' in junctions else junctions[0] + pos_sumo = traci.junction.getPosition(main_junction) + + # IMPORTANT: Convert SUMO coordinates back to CARLA coordinates + # SUMO applies netOffset (99.80, 100.00) during conversion + # To get CARLA coordinates: carla_coord = sumo_coord - offset + net_offset = (99.80, 100.00) + pos_carla = (pos_sumo[0] - net_offset[0], pos_sumo[1] - net_offset[1]) + + # Store in CARLA coordinate system for consistency with state features + self.intersection_center = pos_carla + + logger.info(f"Intersection center (CARLA coords): {self.intersection_center} from SUMO junction {main_junction} at {pos_sumo}") + logger.debug(f"Available junctions: {junctions}") + + def _initialize_traffic_manager(self): + """Initialize MARLTrafficManager with SUMO adapter.""" + logger.info("Initializing MARL Traffic Manager for SUMO") + + try: + # Create SUMO world adapter + sumo_world = SumoWorld() + logger.debug("SUMO world adapter created") + + # Get traffic configuration + traffic_config = self.scenario_config.get('traffic', {}) + logger.debug(f"Traffic config: {traffic_config}") + + # Create state dict for traffic manager + state = { + 'step': 0, + 'episode': 0 + } + + # Initialize traffic manager with SUMO world + logger.debug("Creating MARLTrafficManager...") + self.traffic_manager = MARLTrafficManager( + world=sumo_world, + config=traffic_config, + state=state, + fix_dlt=self.step_length + ) + logger.debug("MARLTrafficManager created") + + # Initialize vehicle spawner + self.vehicle_spawner = SumoVehicleSpawner() + logger.debug("Vehicle spawner created") + + logger.success(f"Traffic Manager initialized with {self.traffic_manager.total_events} spawn events") + + except Exception as e: + logger.error(f"Failed to initialize traffic manager: {e}") + import traceback + traceback.print_exc() + raise + + def _default_reward_params(self) -> Dict: + """Default reward parameters matching CARLA MARLEnv.""" + return { + 'collision': -500.0, + 'success': 400.0, + 'step_penalty': -1.5, + 'speed_bonus': 0.5, + 'speed_threshold': 45.0, # km/h + } + + def step(self) -> Dict[str, Any]: + """ + Execute one simulation step. + + Returns: + Dict containing rewards and metrics + """ + # Spawn vehicles from traffic manager + if self.use_traffic_manager and self.traffic_manager: + spawn_events = self.traffic_manager.update(self.current_step) + if spawn_events: + self.vehicle_spawner.spawn_vehicles(spawn_events) + + # Get current observations + observations = self._get_observations() + + if not observations: + # No active vehicles yet - step simulation + traci.simulationStep() + self.current_step += 1 + self.total_steps += 1 + return {'rewards': {}, 'done': self.current_step >= self.max_steps} + + # Compute actions from MARL manager + target_speeds = self.marl_manager.compute_actions( + observations, training=self.is_training_mode) + + # Apply actions to SUMO vehicles + self._apply_actions(target_speeds) + + # Step SUMO simulation + traci.simulationStep() + + # Check for events (collisions, arrivals) + events = self._check_events() + + # Calculate rewards for the current transition (state -> action -> next_state) + rewards = self._calculate_rewards(events, observations) + + # Get next observations for learning + next_observations = self._get_observations() + + # Update MARL algorithm with CURRENT step's transition + # Transition: (observations, action, reward, next_observations) + # - observations: state before action (S_t) + # - action: stored in last_actions during compute_actions + # - reward: calculated for taking action from observations + # - next_observations: state after action (S_t+1) + if self.is_training_mode: + self.marl_manager.update( + rewards=rewards, + observations=observations, # Use current observations, not previous! + next_observations=next_observations + ) + + # Store for metrics/debugging only (not used for learning anymore) + self.previous_observations = observations.copy() + self.current_step_rewards = rewards + + # Update step counters + self.current_step += 1 + self.total_steps += 1 + + # Update metrics with observations for traffic performance tracking + self._update_metrics(rewards, events, observations) + + # Check if episode is done + done = self.current_step >= self.max_steps or not next_observations + + return { + 'rewards': rewards, + 'events': events, + 'done': done, + 'observations': next_observations + } + + def _get_observations(self) -> Dict[int, Dict]: + """ + Extract observations from SUMO. + + Returns same format as CARLA MARLEnv for compatibility. + """ + observations = {} + + # Get all vehicle IDs in simulation + vehicle_ids = traci.vehicle.getIDList() + + for veh_id in vehicle_ids: + # Skip if vehicle has terminated + if veh_id in self.terminal_agents: + continue + + try: + # Get vehicle state from SUMO + pos = traci.vehicle.getPosition(veh_id) + speed_ms = traci.vehicle.getSpeed(veh_id) + speed_kmh = speed_ms * 3.6 # Convert m/s to km/h + angle_deg = traci.vehicle.getAngle(veh_id) + angle_rad = math.radians(angle_deg) + + # Get leader vehicle + leader_info = traci.vehicle.getLeader(veh_id, 100.0) # Look ahead 100m + if leader_info is not None: + distance_to_front_vehicle = leader_info[1] + else: + distance_to_front_vehicle = 999.0 # No vehicle ahead + + # Calculate relative position to intersection + rel_x = pos[0] - self.intersection_center[0] + rel_y = pos[1] - self.intersection_center[1] + + # Calculate distance to intersection + distance_to_intersection = math.sqrt(rel_x**2 + rel_y**2) + + # Get lane position (simplified) + # 0 = at intersection, 1 = left lane, 2 = middle, 3 = right lane + lane_id = traci.vehicle.getLaneID(veh_id) + lane_index = traci.vehicle.getLaneIndex(veh_id) + lane_position = lane_index if distance_to_intersection < 10 else lane_index + 1 + + # Build observation dictionary (matching CARLA format) + observations[veh_id] = { + 'location': {'x': pos[0], 'y': pos[1], 'z': 0.0}, + 'speed': speed_kmh, + 'heading_angle': angle_rad, + 'relative_position_to_intersection': {'x': rel_x, 'y': rel_y}, + 'distance_to_intersection': distance_to_intersection, + 'distance_to_front_vehicle': distance_to_front_vehicle, + 'lane_position': lane_position, + } + + # Track active vehicle + if veh_id not in self.active_vehicles: + self.active_vehicles[veh_id] = { + 'spawn_step': self.current_step, + 'collisions': 0 + } + + except traci.exceptions.TraCIException as e: + logger.warning(f"Failed to get observation for vehicle {veh_id}: {e}") + continue + + return observations + + def _apply_actions(self, target_speeds: Dict[str, float]): + """ + Apply MARL actions to SUMO vehicles. + + Args: + target_speeds: Dict[vehicle_id, speed_kmh] + """ + for veh_id, speed_kmh in target_speeds.items(): + try: + # Convert km/h to m/s + speed_ms = speed_kmh / 3.6 + + # Clip to reasonable range + speed_ms = max(0.0, min(speed_ms, 30.0)) # Max ~108 km/h + + # Apply speed to SUMO vehicle + traci.vehicle.setSpeed(veh_id, speed_ms) + + except traci.exceptions.TraCIException as e: + logger.warning(f"Failed to set speed for vehicle {veh_id}: {e}") + + def _check_events(self) -> List[StepEvent]: + """ + Check for events (collisions, arrivals). + + Returns: + List of StepEvent objects + """ + events = [] + + # Check for arrived vehicles + arrived_vehicles = traci.simulation.getArrivedIDList() + for veh_id in arrived_vehicles: + if veh_id not in self.departed_vehicles: + self.departed_vehicles.add(veh_id) + self.terminal_agents.add(veh_id) + events.append(StepEvent( + step=self.current_step, + event_id=f'success_{veh_id}_{self.current_step}', + vehicle_id=veh_id, + event_type='success' + )) + + # Check for collisions + collisions = traci.simulation.getCollisions() + for collision in collisions: + # Get colliding vehicle IDs + collider = collision.collider + victim = collision.victim + + for veh_id in [collider, victim]: + if veh_id and veh_id not in self.collided_vehicles: + self.collided_vehicles.add(veh_id) + self.terminal_agents.add(veh_id) + events.append(StepEvent( + step=self.current_step, + event_id=f'collision_{veh_id}_{self.current_step}', + vehicle_id=veh_id, + event_type='collision' + )) + + return events + + def _calculate_rewards(self, events: List[StepEvent], observations: Dict) -> Dict[str, float]: + """ + Calculate rewards from events and observations. + + Matches CARLA MARLEnv reward structure for transfer learning. + """ + rewards = {} + + # Process terminal events first + for event in events: + agent_id = event.vehicle_id + + if event.event_type == 'collision': + rewards[agent_id] = self.reward_params['collision'] + elif event.event_type == 'success': + rewards[agent_id] = self.reward_params['success'] + + # Add step penalties and bonuses for non-terminal agents + for agent_id, obs in observations.items(): + if agent_id in self.terminal_agents: + continue # Skip if already terminated + + # Step penalty (encourages faster completion) + reward = self.reward_params['step_penalty'] + + # Speed bonus (encourages maintaining speed) + if obs['speed'] >= self.reward_params['speed_threshold']: + reward += self.reward_params['speed_bonus'] + + rewards[agent_id] = reward + + return rewards + + def _update_metrics(self, rewards: Dict, events: List[StepEvent], observations: Dict = None): + """Update training metrics.""" + # Count successes this step for accurate episode_length tracking + step_successes = sum(1 for e in events if e.event_type == 'success') + + # Update step metrics with all agent rewards and observations for traffic tracking + self.metrics.update_step(rewards, observations, step_successes=step_successes) + + # Track events + for event in events: + if event.event_type == 'collision': + self.metrics.collisions += 1 + elif event.event_type == 'success': + self.metrics.successes += 1 + + def reset(self): + """Reset the environment for a new episode.""" + logger.info(f"Resetting episode {self.current_episode}") + + # Remove all vehicles + for veh_id in list(traci.vehicle.getIDList()): + try: + traci.vehicle.remove(veh_id) + except traci.exceptions.TraCIException: + pass + + # Reset state + self.current_step = 0 + self.active_vehicles.clear() + self.departed_vehicles.clear() + self.collided_vehicles.clear() + self.terminal_agents.clear() + self.previous_observations.clear() + self.episode_events.clear() + + # Reset traffic manager for new episode + if self.use_traffic_manager and self.traffic_manager: + self.traffic_manager.reset() + + # Log episode metrics before reset (use finish_episode to compute traffic metrics before reset) + if self.current_episode > 0: + # Create episode states snapshot for finish_episode + episode_states = { + 'step': self.current_step, + 'collision': self.collision_count, + 'success': self.success_count, + 'fixed_dt': self.step_length, # For throughput calculation + } + # finish_episode computes traffic metrics (including max_speed) BEFORE resetting + episode_metrics = self.metrics.finish_episode(episode_states) + logger.info(f"Episode {self.current_episode} metrics: {episode_metrics}") + + # Reset MARL algorithm and log episode metrics to TensorBoard + self.marl_manager.reset_episode(episode_metrics=episode_metrics) + + # Save checkpoint periodically + if self.is_training_mode and self.checkpoint_manager: + save_freq = self.training_config.get('save_freq', 10) + if self.current_episode % save_freq == 0: + self._save_checkpoint() + + self.current_episode += 1 + + # Step SUMO a few times to spawn initial vehicles + for _ in range(10): + traci.simulationStep() + + def close(self): + """Close the SUMO connection.""" + logger.info("Closing SUMO environment") + try: + traci.close() + except: + pass + + def get_episode_metrics(self) -> Dict: + """Get current episode metrics (includes traffic metrics like max_speed).""" + metrics = self.metrics.get_current_metrics() + # Add fixed_dt for evaluation manager + metrics['fixed_dt'] = self.step_length + return metrics + + def get_current_step_rewards(self) -> Dict[int, float]: + """Get rewards from the current step for evaluation.""" + return self.current_step_rewards.copy() + + def reset_episode(self) -> Dict: + """ + Reset episode and return metrics (alias for coordinator compatibility). + Returns episode metrics before resetting. + + Note: This uses get_current_metrics() which returns metrics without + resetting. The actual reset happens in self.reset(). + """ + metrics = self.get_episode_metrics() + self.reset() + return metrics + + def _save_checkpoint(self): + """Save training checkpoint.""" + # Use MARLManager's built-in checkpoint saving + checkpoint_dir = self.config.get('meta', {}).get('checkpoint_dir', 'checkpoints') + scenario_type = self.config.get('meta', {}).get('scenario_type', 'intersection_sumo') + checkpoint_path = f"{checkpoint_dir}/{scenario_type}/td3_episode_{self.current_episode}.pth" + + import os + os.makedirs(os.path.dirname(checkpoint_path), exist_ok=True) + + self.marl_manager.save_checkpoint(checkpoint_path) + logger.info(f"Checkpoint saved at episode {self.current_episode}: {checkpoint_path}") + + def _load_checkpoint_from_config(self, checkpoint_path: str): + """Load checkpoint from file.""" + logger.info(f"Loading checkpoint from: {checkpoint_path}") + + checkpoint_data = self.checkpoint_manager.load(checkpoint_path) + + if checkpoint_data: + self.marl_manager.load_checkpoint_data(checkpoint_data) + self.current_episode = checkpoint_data.get('episode', 0) + self.total_steps = checkpoint_data.get('total_steps', 0) + logger.info(f"Resumed from episode {self.current_episode}, step {self.total_steps}") + else: + logger.warning(f"Failed to load checkpoint: {checkpoint_path}") diff --git a/opencda_marl/scenarios/scenario_manager.py b/opencda_marl/scenarios/scenario_manager.py index 53760de..083ba29 100644 --- a/opencda_marl/scenarios/scenario_manager.py +++ b/opencda_marl/scenarios/scenario_manager.py @@ -88,6 +88,13 @@ def __init__( self.states['max_episodes'] = 1 logger.info("Baseline agent, setting max episode to 1.") + # Override max_episodes for MARL evaluation mode (training_mode: false) + marl_cfg = scenario_params.get('MARL', {}) + training_mode = marl_cfg.get('training', {}).get('training_mode', True) + if not training_mode and self.agent_manager.agent_type == 'marl': + self.states['max_episodes'] = 1 + logger.info("Evaluation mode: setting max episode to 1 (training_mode: false)") + logger.success( "MARLScenarioManager initialized (CARLA-only, all CAV).") @@ -97,7 +104,8 @@ def __init__( def get_traffic_info(self) -> Dict[str, Any]: return { 'queue_count': self.agent_manager.get_queue_count(), - 'agent_type': self.agent_manager.agent_type + 'agent_type': self.agent_manager.agent_type, + 'pending_spawns': self.traffic_manager.total_events } def get_observations(self) -> Dict[str, Any]: @@ -156,12 +164,18 @@ def reset_episode(self): self.states['collision'] = 0 self.states['success'] = 0 self.states['active_agents'] = 0 - + # Reset agent manager self.agent_manager.reset() # Reset traffic manager self.traffic_manager.reset() + # Tick world to ensure CARLA processes cleanup and releases GPU memory + try: + self.world.tick() + except Exception: + pass + def reset(self): """Reset scenario manager for new episode. @@ -178,6 +192,12 @@ def reset(self): # Reset traffic manager self.traffic_manager.reset() + # Tick world to ensure CARLA processes cleanup and releases GPU memory + try: + self.world.tick() + except Exception: + pass + logger.warning("MARLScenarioManager reset completed") def close(self): diff --git a/pixi.lock b/pixi.lock index f44e8d0..e3e693a 100644 --- a/pixi.lock +++ b/pixi.lock @@ -7,40 +7,417 @@ environments: - url: https://conda.anaconda.org/nvidia/ indexes: - https://pypi.org/simple + options: + pypi-prerelease-mode: if-necessary-or-explicit packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-7_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.13.2-py310h5541b7b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.14-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/assimp-5.4.3-hecf2907_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/async-timeout-5.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.2-h39aace5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py310hba01987_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py310he7384ee_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cli11-2.6.0-h54a6638_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.2-py310h3788b33_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.10.19-py310hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dash-3.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/embree-4.4.0-ha751380_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ffmpeg-8.0.0-gpl_hbbdf940_906.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/flask-3.1.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fluidsynth-2.3.7-hd992666_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-11.2.0-h07f6e7f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.60.1-py310h3406613_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freeglut-3.2.2-ha6d2627_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.7.0-py310h9548a50_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.4-h2b0a6b4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-tools-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.45-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gl2ps-1.4.2-hae5d5c5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glew-2.2.0-h3abd4de_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glfw-3.4-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glslang-16.0.0-hfd11570_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hac33072_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.74.1-py310h8f3b6d6_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py310h4aa865e_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.2.0-h15599e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/imath-3.2.2-hde8ca8f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/intel-gmmlib-22.8.2-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/intel-media-driver-25.3.4-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/itsdangerous-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jack-1.9.22-hf4617a5_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jasper-4.2.8-he3c4edf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jsoncpp-1.9.6-hf42df4d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py310haaf941d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/level-zero-1.26.0-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-devel-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libass-0.17.4-h96ad9f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.3.0-h6395336_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-2_h5875eb1_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-1.88.0-hed09d94_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.77-h3ff7636_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-2_hfef963f_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.6-default_h99862b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.6-default_h746c552_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.2-h32235b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglu-9.0.3-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.74.1-h3288cfb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.1-default_h7f8ec31_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.3.0-h4c17acf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.1-hf08fa70_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-2_h5e43f62_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-2_hdba1596_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.6-hf7376ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzf-3.6-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmad-0.15.1b-h0b41bf4_1001.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.3-nompi_h11f7409_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.5-hd0c01bc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopencv-4.12.0-qt6_py310hdb0ca46_607.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-auto-batch-plugin-2025.2.0-hed573e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-auto-plugin-2025.2.0-hed573e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-hetero-plugin-2025.2.0-hd41364c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-cpu-plugin-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-gpu-plugin-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-npu-plugin-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-ir-frontend-2025.2.0-hd41364c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-onnx-frontend-2025.2.0-h1862bb8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-paddle-frontend-2025.2.0-h1862bb8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-pytorch-frontend-2025.2.0-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-frontend-2025.2.0-h0767aad_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-lite-frontend-2025.2.0-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopus-1.5.2-hd0c01bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.51-h421ea60_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.1-h5c52fec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h7b12aa8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.60.0-h61e6d4b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.0-hee844dc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.10-hd0affe5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtheora-1.1.1-h4ab18f5_1006.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libudev1-257.10-hd0affe5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liburing-2.12-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libusb-1.0.29-h73b1eb8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libva-2.22.0-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h54a6638_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvpl-2.15.0-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvpx-1.14.1-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.0-hca5e8e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzip-1.11.2-h6991a6a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-21.1.6-h4922eb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/loguru-0.7.3-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lxml-6.0.2-py310he6d4be0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py310h3406613_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.8-py310hff52083_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py310hfde16b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2025.3.0-h0e700b2_462.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.9-hc50e24c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py310h03d9f68_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.6.3-py310h89163eb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.12.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.4.2-pyh267e887_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.6-py310hefbff90_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/open3d-0.19.0-py310h757a272_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/opencv-4.12.0-qt6_py310hec850f8_607.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openexr-3.4.4-he10986b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openh264-2.6.0-hc22cd8d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjph-0.25.3-h8d634f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/opusfile-0.12-h3358134_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py310h0158d43_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hadf4263_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.46-h1321c63_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py310h6557065_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.3-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/polars-1.35.2-pyh6a1acc5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-32-1.35.2-py310hffdcd12_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/portaudio-19.6.0-h7c63dc7_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/portmidi-2.0.7-h3cb78e3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.0-hb72c0af_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.3.1-py310h89163eb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.31.1-py310ha36e12e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.3-py310h139afa4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pugixml-1.15-h3f63f65_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-h9a6aba3_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/py-cpuinfo-9.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/py-opencv-4.12.0-qt6_py310h89973df_607.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-4-hd8ed1ab_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pyclean-3.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pygame-2.6.1-py310hae03094_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.9.3-py310h2007e60_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.10.19-h3c07f61_2_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.10.19-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py310h3406613_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.3-h5c1c036_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.7.1-h8fae777_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/retrying-1.4.2-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.7.2-py310h228f341_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py310h1d65ade_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2-2.32.56-h54a6638_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2_image-2.8.2-h06ee604_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2_mixer-2.6.3-h8830914_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2_ttf-2.24.0-h287479f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl3-3.2.26-h68140b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/shaderc-2025.4-h3e344bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py310hc8bbb35_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/spirv-tools-2025.4-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.51.0-heff268d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.5-py310hf779ad0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/svt-av1-3.1.2-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2022.3.0-h8d10470_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.20.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorboard-data-server-0.7.0-py310hed992bd_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tinyobjloader-1.0.7-h59595ed_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py310h7c4b9e2_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ultralytics-8.3.232-pyh865f8e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.0-py310h7c4b9e2_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/utfcpp-4.0.8-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.1-py310h991dc19_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wayland-protocols-1.46-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wslink-2.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/x264-1!164.3095-h166bdaf_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/x265-3.5-h924138e_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.46-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxscrnsaver-1.2.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.22.0-py310h3406613_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py310h139afa4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + - pypi: https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/5a/99/210e113dde53955e97042bd76dc4ad927eca04c5b4645ec157cc59f4f3ae/nvidia_cublas-13.0.0.19-py3-none-manylinux_2_27_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ba/28/e37d62ff27b4462953fdd5713d8a78760578dfa12685c30b71b55fab57b1/nvidia_cuda_cupti-13.0.48-py3-none-manylinux_2_25_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/be/5b/f7636b3d66caefade6a0a0dc5b705c259a2062c20ad18b432b3129d348e0/nvidia_cuda_nvrtc-13.0.48-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/cc/78/edb119083ca2ff0f09ab0cd597e97775ac3f575b8aa0caf10d68ed49e032/nvidia_cuda_runtime-13.0.48-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/cf/68/2712854561170b2a81bea7b6b35cc1ae264d9794c0c218986e5c685d45f7/nvidia_cudnn_cu13-9.13.0.50-py3-none-manylinux_2_27_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/9b/9f/e298b66e584ad25bd78ad4a45b061fe7bb57a1ec011128089404ce3fcc7d/nvidia_cufft-12.0.0.15-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ad/0a/4adf0c9bb1241cd1314fc923fde00f3749c7fc785b1e3b3f4a104cd3090c/nvidia_cufile-1.15.0.42-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a5/87/e3c9ee227b750e5b61572e7509f586cc8d494a4f7874b5163e734ed852c2/nvidia_cusolver-12.0.3.29-py3-none-manylinux_2_27_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ba/e8/b3f7a87cc719dca926c7baee92f2544de8909573a4126c85a9f1625431e8/nvidia_cusparse-12.6.2.49-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/f1/3a/dabb10684e60edfaf1a1c9984d12a668bc1091582099d4e03ac5b9983b51/nvidia_nccl_cu13-2.27.7-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/95/39/726edebeb76f3efc25c79f885429fa1227c9d200e20ea219bf724b382e19/nvidia_nvjitlink-13.0.39-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/6f/e9/8530afb8ed38d16bbc89cec80a4dd6a52dbf59bc93e546c3658cfa8b1f9b/nvidia_nvshmem_cu13-3.3.24-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/38/37/0d103c84e7884382a79a569b720965141f83dd1c5df9e3e00cbc02d7099c/nvidia_nvtx-13.0.39-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/1a/48/39dbcca267b826eaf7b0edc1f0e78f85698b4132393a894fe58656535927/sumolib-1.25.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl + - pypi: https://download.pytorch.org/whl/cu130/torch-2.9.1%2Bcu130-cp310-cp310-manylinux_2_28_x86_64.whl + - pypi: https://download.pytorch.org/whl/cu130/torchvision-0.24.1%2Bcu130-cp310-cp310-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/fd/6e/676ab5019b4dde8b9b7bab71245102fc02778ef3df48218b298686b9ffd6/triton-3.5.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: ./dependencies/whl/carla-0.9.15-cp310-cp310-manylinux_2_27_x86_64.whl win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/_libavif_api-1.3.0-h57928b3_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-2_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aiohttp-3.12.15-py310hdb0e946_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aiohttp-3.13.2-py310h535af70_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/aom-3.9.1-he0c23c2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/assimp-5.4.3-hfc39600_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/async-timeout-5.0.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.1.0-h2466b09_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.1.0-h2466b09_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.1.0-py310h9e98ed7_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h2466b09_7.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.8.3-h4c7d964_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-h2d644bc_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py310hfff998d_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.5-h2466b09_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h5782bbf_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.8.3-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-1.17.1-py310ha8f682b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.3-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.2.1-pyh7428d3b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py310h29418f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cli11-2.6.0-h5112557_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh7428d3b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.2-py310hc19bc0b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.10.19-py310hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dash-3.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dash-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/dav1d-1.2.1-hcfcfb64_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.3.1-he0c23c2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/embree-4.4.0-ha827246_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ffmpeg-7.1.1-gpl_h70aa942_909.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ffmpeg-8.0.0-gpl_he3062b8_906.conda - conda: https://conda.anaconda.org/conda-forge/noarch/flask-3.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-11.2.0-h1d4551f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 @@ -49,38 +426,41 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.15.0-h765892d_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/win-64/fonttools-4.59.2-py310hdb0e946_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fonttools-4.60.1-py310hdb0e946_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/freeglut-3.2.2-he0c23c2_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.13.3-h57928b3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.10-h8d14728_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.1-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/frozenlist-1.7.0-py310had1666a_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/gdk-pixbuf-2.42.12-h1f5b9c4_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.13.1-h9ea8674_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/gdk-pixbuf-2.44.4-h1f5b9c4_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-hdade9fe_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.45-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/gl2ps-1.4.2-had7236b_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/glew-2.1.0-h39d44d4_2.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/win-64/glfw-3.5.0-hfd05255_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/glew-2.2.0-hcc5dbe9_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/glfw-3.4-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/glslang-16.0.0-h5b34520_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.14-hac47afa_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/h5py-3.14.0-nompi_py310h877c39c_100.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-11.4.4-h5f2951f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/grpcio-1.74.1-py310hc77e67f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/h5py-3.15.1-nompi_py310hb7e4da9_101.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-12.2.0-h5f2951f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/hdf4-4.2.15-h5557f11_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/hdf5-1.14.6-nompi_he30205f_103.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/icu-75.1-he0c23c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/imath-3.2.1-h1608b31_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/imath-3.2.2-h1608b31_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda - conda: https://conda.anaconda.org/conda-forge/noarch/itsdangerous-2.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/jasper-4.2.8-h8ad263b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/jsoncpp-1.9.6-hda1637e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/khronos-opencl-icd-loader-2024.10.24-h2466b09_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.4.9-py310h1e1005b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.4.9-py310h1e1005b_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.21.3-hdf4eb48_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lame-3.100-hcfcfb64_1003.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.17-hbcf6048_0.conda @@ -89,38 +469,39 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libaec-1.1.4-h20038f6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libasprintf-0.22.5-h5728263_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libavif16-1.3.0-he916da2_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.9.0-34_h5709861_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libboost-1.88.0-h9dfe17d_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.1.0-h2466b09_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.1.0-h2466b09_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.1.0-h2466b09_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.9.0-34_h2a3cdd5_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-20.1.8-default_hadf22e1_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.14.1-h88aaa65_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.24-h76ddb4d_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.1-hac47afa_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.4.6-h537db12_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.9.0-35_h5709861_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libboost-1.88.0-h9dfe17d_6.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.9.0-35_h2a3cdd5_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-21.1.6-default_ha2db4b5_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.17.0-h43ecb02_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h51727cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libflac-1.4.3-h63175ca_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.13.3-h57928b3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.13.3-h0b5ce68_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-15.1.0-h1383e82_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.1-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.1-hdbac1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-15.2.0-h8ee18e1_13.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgettextpo-0.22.5-h5728263_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.84.3-h1c1036b_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-15.1.0-h1383e82_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h88281d1_1000.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.3.0-h47aaa27_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.86.2-hd9c3897_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-15.2.0-h8ee18e1_13.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.74.1-h317e13b_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h64bd3f2_1002.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.3.0-ha71e874_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.0-h2466b09_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.11.1-h98f49f0_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.9.0-34_hf9ab0e9_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/liblapacke-3.9.0-34_h3ae206f_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.2-hfd05255_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.11.1-hac9b6f3_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.9.0-35_hf9ab0e9_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblapacke-3.9.0-35_h3ae206f_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzf-3.6-h2466b09_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmad-0.15.1b-hcfcfb64_1001.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libnetcdf-4.9.2-nompi_ha45073a_118.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libnetcdf-4.9.3-nompi_h7d90bef_103.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libogg-1.3.5-h2466b09_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libopencv-4.12.0-qt6_py310h5bb264c_603.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libopencv-4.12.0-qt6_py310h3c58159_607.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-2025.2.0-hbf28c98_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-auto-batch-plugin-2025.2.0-hdd9a157_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-auto-plugin-2025.2.0-hdd9a157_1.conda @@ -134,141 +515,155 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-tensorflow-frontend-2025.2.0-h293fe96_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-tensorflow-lite-frontend-2025.2.0-hac47afa_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopus-1.5.2-h2466b09_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.50-h7351971_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-6.31.1-hdcda5b4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/librsvg-2.58.4-h5ce5fed_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.51-h7351971_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-6.31.1-hdcda5b4_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2025.11.05-h0eb2380_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/librsvg-2.60.0-hd5e4115_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.20-hc70643c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.50.4-hf5d6505_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.0-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libtheora-1.1.1-hc70643c_1006.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.0-h550210a_6.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.1-h8f73337_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libusb-1.0.29-h1839187_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libvorbis-1.3.7-h5112557_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libvulkan-loader-1.4.328.1-h477610d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_9.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.13.8-h741aa76_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h25c3957_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.1-h06f855e_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-ha29bfb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h0fbe4c1_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzip-1.11.2-h3135430_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-20.1.8-hfa2b4ca_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/loguru-0.7.3-pyh7428d3b_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/lxml-6.0.1-py310h4b5876d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lxml-6.0.2-py310h095aac5_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h2466b09_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.2-py310h38315fa_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.5-py310h5588dad_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.5-py310h0bdd906_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py310hdb0e946_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.8-py310h5588dad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.8-py310h0bdd906_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.0.10-h9fa1bad_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2024.2.2-h57928b3_16.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2024.2.2-h57928b3_15.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mpg123-1.32.9-h01009b0_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.1.1-py310hc19bc0b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.1.2-py310he9f1925_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/multidict-6.6.3-py310hdb0e946_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.4.2-pyh267e887_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-he0c23c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.2.6-py310h4987827_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/open3d-0.19.0-py310hadc9e84_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/open3d-0.19.0-py310hc9aaaf8_6.conda - conda: https://conda.anaconda.org/conda-forge/win-64/opencl-headers-2025.06.13-he0c23c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/opencv-4.12.0-qt6_py310he543868_603.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/openexr-3.3.5-hed76565_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/opencv-4.12.0-qt6_py310he543868_607.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openexr-3.4.4-ha75af49_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openh264-2.6.0-hb17fa0b_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.3-h24db6dd_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.5.2-h725018a_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h24db6dd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openjph-0.25.3-hf13a347_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.3.2-py310hed136d8_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.3.3-py310hed136d8_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.56.4-h03d888a_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.45-h99c9b8b_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-11.3.0-py310h6d647b9_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.46-h3402e2f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-11.3.0-py310hb3a2f59_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.3-pyh8b19718_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.3.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/portmidi-2.0.6-hac47afa_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.6.2-h7990399_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/polars-1.35.2-pyh6a1acc5_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/polars-runtime-32-1.35.2-py310hca7251b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/portmidi-2.0.7-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.7.0-h9080b7b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/propcache-0.3.1-py310h38315fa_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.0.0-py310h29418f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/protobuf-6.31.1-py310h81b7714_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.1.3-py310h1637853_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pugixml-1.15-h372dad0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/py-cpuinfo-9.0.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/py-opencv-4.12.0-qt6_py310h9217539_603.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/py-opencv-4.12.0-qt6_py310h9217539_607.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-4-hd8ed1ab_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pyclean-3.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pygame-2.6.1-py310hea55160_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.1-py310h2d19612_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.3-py310h96c60bd_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.10.18-h8c5b53a_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.10.19-hc20f281_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.10.19-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.2-py310h38315fa_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py310hdb0e946_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.1-h02ddd7d_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.3-ha0de62e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/rav1e-0.7.1-ha073cba_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-ha104f34_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/retrying-1.4.2-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.7.1-py310h21054b0_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.7.2-py310h21054b0_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.15.2-py310h15c175c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2-2.32.54-he0c23c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2-2.32.56-h5112557_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2_image-2.8.2-h5ca362a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2_mixer-2.6.3-he8f0768_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2_ttf-2.24.0-hd2a6e60_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/sdl3-3.2.20-h5112557_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2_ttf-2.24.0-hb42d5a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/sdl3-3.2.26-h5112557_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.1-py310hb052cae_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/shaderc-2025.4-haa9a63f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.2-py310h62de375_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.50.4-hdb435a2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/statsmodels-0.14.5-py310h8f3aa81_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/spirv-tools-2025.4-h49e36cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.51.0-hdb435a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/statsmodels-0.14.5-py310h8f3aa81_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/svt-av1-3.1.2-hac47afa_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2021.13.0-h18a62a1_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2021.13.0-hd094cb3_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.20.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tensorboard-data-server-0.7.0-py310h5588dad_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tinyobjloader-1.0.7-h63175ca_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.2-py310h29418f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.2-py310h29418f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.22621.0-h57928b3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ultralytics-8.3.186-pyh2a12c56_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-16.0.0-py310ha8f682b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ultralytics-8.3.232-pyh865f8e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.0-py310h29418f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/utfcpp-4.0.6-hc1507ef_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h41ae7f8_31.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_31.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_31.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.44.35208-h38c0c73_31.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vtk-base-9.4.2-py310h27c0482_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/utfcpp-4.0.8-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h2b53caa_32.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_32.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_32.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.44.35208-h38c0c73_32.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vtk-base-9.5.1-py310hcc07014_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/win32_setctime-1.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/wslink-2.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wslink-2.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/x264-1!164.3095-h8ffe710_2.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/x265-3.5-h2d74725_3.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-h0e40799_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-h0e40799_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/yarl-1.20.1-py310h38315fa_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/zeromq-4.3.5-ha9f60a1_7.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/yarl-1.22.0-py310hdb0e946_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zeromq-4.3.5-h5bddc39_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.1-h2466b09_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.23.0-py310h29418f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py310h1637853_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-hbeecb71_2.conda - - pypi: https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/2f/e0/014d5d9d7a4564cf1c40b5039bc882db69fd881111e03ab3657ac0b218e2/fsspec-2025.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f0/35/9c524281aab9550187d638862ceb663cb2d31fe04bb630aeb109faf81432/sumolib-1.24.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1a/48/39dbcca267b826eaf7b0edc1f0e78f85698b4132393a894fe58656535927/sumolib-1.25.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl - - pypi: https://download.pytorch.org/whl/nightly/cu128/torch-2.9.0.dev20250827%2Bcu128-cp310-cp310-win_amd64.whl - - pypi: https://download.pytorch.org/whl/nightly/cu128/torchvision-0.24.0.dev20250827%2Bcu128-cp310-cp310-win_amd64.whl + - pypi: https://download.pytorch.org/whl/nightly/cu128/torch-2.10.0.dev20251124%2Bcu128-cp310-cp310-win_amd64.whl + - pypi: https://download.pytorch.org/whl/nightly/cu128/torchvision-0.25.0.dev20251124%2Bcu128-cp310-cp310-win_amd64.whl - pypi: ./dependencies/whl/carla-0.9.15-cp310-cp310-win_amd64.whl docs: channels: @@ -277,42 +672,441 @@ environments: - url: https://conda.anaconda.org/nvidia/ indexes: - https://pypi.org/simple + options: + pypi-prerelease-mode: if-necessary-or-explicit packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-7_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.13.2-py310h5541b7b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.14-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/assimp-5.4.3-hecf2907_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/async-timeout-5.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.2-h39aace5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backrefs-5.8-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py310hba01987_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py310he7384ee_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cli11-2.6.0-h54a6638_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.2-py310h3788b33_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.10.19-py310hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dash-3.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/embree-4.4.0-ha751380_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ffmpeg-8.0.0-gpl_hbbdf940_906.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/flask-3.1.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fluidsynth-2.3.7-hd992666_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-11.2.0-h07f6e7f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.60.1-py310h3406613_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freeglut-3.2.2-ha6d2627_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.7.0-py310h9548a50_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.4-h2b0a6b4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-tools-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ghp-import-2.1.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.45-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gl2ps-1.4.2-hae5d5c5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glew-2.2.0-h3abd4de_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glfw-3.4-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glslang-16.0.0-hfd11570_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hac33072_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.74.1-py310h8f3b6d6_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py310h4aa865e_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.2.0-h15599e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/imath-3.2.2-hde8ca8f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/intel-gmmlib-22.8.2-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/intel-media-driver-25.3.4-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/itsdangerous-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jack-1.9.22-hf4617a5_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jasper-4.2.8-he3c4edf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jsoncpp-1.9.6-hf42df4d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py310haaf941d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/level-zero-1.26.0-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-devel-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libass-0.17.4-h96ad9f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.3.0-h6395336_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-2_h5875eb1_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-1.88.0-hed09d94_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.77-h3ff7636_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-2_hfef963f_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.6-default_h99862b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.6-default_h746c552_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.2-h32235b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglu-9.0.3-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.74.1-h3288cfb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.1-default_h7f8ec31_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.3.0-h4c17acf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.1-hf08fa70_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-2_h5e43f62_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-2_hdba1596_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.6-hf7376ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzf-3.6-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmad-0.15.1b-h0b41bf4_1001.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.3-nompi_h11f7409_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.5-hd0c01bc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopencv-4.12.0-qt6_py310hdb0ca46_607.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-auto-batch-plugin-2025.2.0-hed573e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-auto-plugin-2025.2.0-hed573e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-hetero-plugin-2025.2.0-hd41364c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-cpu-plugin-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-gpu-plugin-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-npu-plugin-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-ir-frontend-2025.2.0-hd41364c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-onnx-frontend-2025.2.0-h1862bb8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-paddle-frontend-2025.2.0-h1862bb8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-pytorch-frontend-2025.2.0-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-frontend-2025.2.0-h0767aad_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-lite-frontend-2025.2.0-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopus-1.5.2-hd0c01bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.51-h421ea60_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.1-h5c52fec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h7b12aa8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.60.0-h61e6d4b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.0-hee844dc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.10-hd0affe5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtheora-1.1.1-h4ab18f5_1006.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libudev1-257.10-hd0affe5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liburing-2.12-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libusb-1.0.29-h73b1eb8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libva-2.22.0-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h54a6638_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvpl-2.15.0-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvpx-1.14.1-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.0-hca5e8e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzip-1.11.2-h6991a6a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-21.1.6-h4922eb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/loguru-0.7.3-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lxml-6.0.2-py310he6d4be0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py310h3406613_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.8-py310hff52083_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py310hfde16b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mergedeep-1.3.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mkdocs-1.6.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mkdocs-get-deps-0.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mkdocs-material-9.7.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mkdocs-material-extensions-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2025.3.0-h0e700b2_462.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.9-hc50e24c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py310h03d9f68_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.6.3-py310h89163eb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.12.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.4.2-pyh267e887_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.6-py310hefbff90_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/open3d-0.19.0-py310h757a272_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/opencv-4.12.0-qt6_py310hec850f8_607.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openexr-3.4.4-he10986b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openh264-2.6.0-hc22cd8d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjph-0.25.3-h8d634f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/opusfile-0.12-h3358134_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paginate-0.5.7-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py310h0158d43_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hadf4263_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.46-h1321c63_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py310h6557065_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.3-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/polars-1.35.2-pyh6a1acc5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-32-1.35.2-py310hffdcd12_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/portaudio-19.6.0-h7c63dc7_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/portmidi-2.0.7-h3cb78e3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.0-hb72c0af_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.3.1-py310h89163eb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.31.1-py310ha36e12e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.3-py310h139afa4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pugixml-1.15-h3f63f65_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-h9a6aba3_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/py-cpuinfo-9.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/py-opencv-4.12.0-qt6_py310h89973df_607.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-4-hd8ed1ab_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pyclean-3.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pygame-2.6.1-py310hae03094_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pymdown-extensions-10.17.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.9.3-py310h2007e60_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.10.19-h3c07f61_2_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.10.19-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py310h3406613_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyyaml-env-tag-1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.3-h5c1c036_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.7.1-h8fae777_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/retrying-1.4.2-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.7.2-py310h228f341_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py310h1d65ade_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2-2.32.56-h54a6638_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2_image-2.8.2-h06ee604_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2_mixer-2.6.3-h8830914_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2_ttf-2.24.0-h287479f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl3-3.2.26-h68140b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/shaderc-2025.4-h3e344bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py310hc8bbb35_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/spirv-tools-2025.4-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.51.0-heff268d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.5-py310hf779ad0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/svt-av1-3.1.2-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2022.3.0-h8d10470_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.20.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorboard-data-server-0.7.0-py310hed992bd_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tinyobjloader-1.0.7-h59595ed_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py310h7c4b9e2_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ultralytics-8.3.232-pyh865f8e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.0-py310h7c4b9e2_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/utfcpp-4.0.8-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.1-py310h991dc19_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/watchdog-6.0.0-py310hff52083_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wayland-protocols-1.46-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wslink-2.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/x264-1!164.3095-h166bdaf_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/x265-3.5-h924138e_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.46-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxscrnsaver-1.2.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.22.0-py310h3406613_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py310h139afa4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + - pypi: https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/96/fd/a40c621ff207f3ce8e484aa0fc8ba4eb6e3ecf52e15b42ba764b457a9550/editorconfig-0.17.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2d/14/1c65fccf8413d5f5c6e8425f84675169654395098000d8bddc4e9d3390e1/jsbeautifier-1.15.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/51/fe0e3fdb16f6eed65c9459d12bae6a4e1f0bb4e2228cb037e7907b002678/mkdocs_git_revision_date_localized_plugin-1.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1a/4b/6fd6dd632019b7f522f1b1f794ab6115cd79890330986614be56fd18f0eb/mkdocs_mermaid2_plugin-1.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/5a/99/210e113dde53955e97042bd76dc4ad927eca04c5b4645ec157cc59f4f3ae/nvidia_cublas-13.0.0.19-py3-none-manylinux_2_27_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ba/28/e37d62ff27b4462953fdd5713d8a78760578dfa12685c30b71b55fab57b1/nvidia_cuda_cupti-13.0.48-py3-none-manylinux_2_25_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/be/5b/f7636b3d66caefade6a0a0dc5b705c259a2062c20ad18b432b3129d348e0/nvidia_cuda_nvrtc-13.0.48-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/cc/78/edb119083ca2ff0f09ab0cd597e97775ac3f575b8aa0caf10d68ed49e032/nvidia_cuda_runtime-13.0.48-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/cf/68/2712854561170b2a81bea7b6b35cc1ae264d9794c0c218986e5c685d45f7/nvidia_cudnn_cu13-9.13.0.50-py3-none-manylinux_2_27_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/9b/9f/e298b66e584ad25bd78ad4a45b061fe7bb57a1ec011128089404ce3fcc7d/nvidia_cufft-12.0.0.15-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ad/0a/4adf0c9bb1241cd1314fc923fde00f3749c7fc785b1e3b3f4a104cd3090c/nvidia_cufile-1.15.0.42-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a5/87/e3c9ee227b750e5b61572e7509f586cc8d494a4f7874b5163e734ed852c2/nvidia_cusolver-12.0.3.29-py3-none-manylinux_2_27_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ba/e8/b3f7a87cc719dca926c7baee92f2544de8909573a4126c85a9f1625431e8/nvidia_cusparse-12.6.2.49-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/f1/3a/dabb10684e60edfaf1a1c9984d12a668bc1091582099d4e03ac5b9983b51/nvidia_nccl_cu13-2.27.7-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/95/39/726edebeb76f3efc25c79f885429fa1227c9d200e20ea219bf724b382e19/nvidia_nvjitlink-13.0.39-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/6f/e9/8530afb8ed38d16bbc89cec80a4dd6a52dbf59bc93e546c3658cfa8b1f9b/nvidia_nvshmem_cu13-3.3.24-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/38/37/0d103c84e7884382a79a569b720965141f83dd1c5df9e3e00cbc02d7099c/nvidia_nvtx-13.0.39-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1a/48/39dbcca267b826eaf7b0edc1f0e78f85698b4132393a894fe58656535927/sumolib-1.25.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl + - pypi: https://download.pytorch.org/whl/cu130/torch-2.9.1%2Bcu130-cp310-cp310-manylinux_2_28_x86_64.whl + - pypi: https://download.pytorch.org/whl/cu130/torchvision-0.24.1%2Bcu130-cp310-cp310-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/fd/6e/676ab5019b4dde8b9b7bab71245102fc02778ef3df48218b298686b9ffd6/triton-3.5.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: ./dependencies/whl/carla-0.9.15-cp310-cp310-manylinux_2_27_x86_64.whl win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/_libavif_api-1.3.0-h57928b3_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-2_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aiohttp-3.12.15-py310hdb0e946_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aiohttp-3.13.2-py310h535af70_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/aom-3.9.1-he0c23c2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/assimp-5.4.3-hfc39600_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/async-timeout-5.0.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backrefs-5.8-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.1.0-h2466b09_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.1.0-h2466b09_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.1.0-py310h9e98ed7_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h2466b09_7.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.8.3-h4c7d964_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-h2d644bc_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py310hfff998d_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.5-h2466b09_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h5782bbf_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.8.3-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-1.17.1-py310ha8f682b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.3-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.2.1-pyh7428d3b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py310h29418f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cli11-2.6.0-h5112557_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh7428d3b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.2-py310hc19bc0b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.10.19-py310hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dash-3.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dash-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/dav1d-1.2.1-hcfcfb64_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.3.1-he0c23c2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/embree-4.4.0-ha827246_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ffmpeg-7.1.1-gpl_h70aa942_909.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ffmpeg-8.0.0-gpl_he3062b8_906.conda - conda: https://conda.anaconda.org/conda-forge/noarch/flask-3.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-11.2.0-h1d4551f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 @@ -321,39 +1115,42 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.15.0-h765892d_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/win-64/fonttools-4.59.2-py310hdb0e946_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fonttools-4.60.1-py310hdb0e946_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/freeglut-3.2.2-he0c23c2_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.13.3-h57928b3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.10-h8d14728_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.1-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/frozenlist-1.7.0-py310had1666a_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/gdk-pixbuf-2.42.12-h1f5b9c4_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.13.1-h9ea8674_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/gdk-pixbuf-2.44.4-h1f5b9c4_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-hdade9fe_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ghp-import-2.1.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.45-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/gl2ps-1.4.2-had7236b_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/glew-2.1.0-h39d44d4_2.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/win-64/glfw-3.5.0-hfd05255_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/glew-2.2.0-hcc5dbe9_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/glfw-3.4-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/glslang-16.0.0-h5b34520_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.14-hac47afa_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/h5py-3.14.0-nompi_py310h877c39c_100.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-11.4.4-h5f2951f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/grpcio-1.74.1-py310hc77e67f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/h5py-3.15.1-nompi_py310hb7e4da9_101.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-12.2.0-h5f2951f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/hdf4-4.2.15-h5557f11_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/hdf5-1.14.6-nompi_he30205f_103.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/icu-75.1-he0c23c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/imath-3.2.1-h1608b31_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/imath-3.2.2-h1608b31_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda - conda: https://conda.anaconda.org/conda-forge/noarch/itsdangerous-2.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/jasper-4.2.8-h8ad263b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/jsoncpp-1.9.6-hda1637e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/khronos-opencl-icd-loader-2024.10.24-h2466b09_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.4.9-py310h1e1005b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.4.9-py310h1e1005b_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.21.3-hdf4eb48_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lame-3.100-hcfcfb64_1003.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.17-hbcf6048_0.conda @@ -362,38 +1159,39 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libaec-1.1.4-h20038f6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libasprintf-0.22.5-h5728263_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libavif16-1.3.0-he916da2_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.9.0-34_h5709861_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libboost-1.88.0-h9dfe17d_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.1.0-h2466b09_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.1.0-h2466b09_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.1.0-h2466b09_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.9.0-34_h2a3cdd5_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-20.1.8-default_hadf22e1_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.14.1-h88aaa65_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.24-h76ddb4d_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.1-hac47afa_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.4.6-h537db12_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.9.0-35_h5709861_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libboost-1.88.0-h9dfe17d_6.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.9.0-35_h2a3cdd5_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-21.1.6-default_ha2db4b5_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.17.0-h43ecb02_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h51727cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libflac-1.4.3-h63175ca_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.13.3-h57928b3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.13.3-h0b5ce68_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-15.1.0-h1383e82_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.1-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.1-hdbac1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-15.2.0-h8ee18e1_13.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgettextpo-0.22.5-h5728263_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.84.3-h1c1036b_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-15.1.0-h1383e82_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h88281d1_1000.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.3.0-h47aaa27_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.86.2-hd9c3897_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-15.2.0-h8ee18e1_13.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.74.1-h317e13b_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h64bd3f2_1002.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.3.0-ha71e874_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.0-h2466b09_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.11.1-h98f49f0_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.9.0-34_hf9ab0e9_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/liblapacke-3.9.0-34_h3ae206f_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.2-hfd05255_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.11.1-hac9b6f3_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.9.0-35_hf9ab0e9_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblapacke-3.9.0-35_h3ae206f_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzf-3.6-h2466b09_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmad-0.15.1b-hcfcfb64_1001.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libnetcdf-4.9.2-nompi_ha45073a_118.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libnetcdf-4.9.3-nompi_h7d90bef_103.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libogg-1.3.5-h2466b09_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libopencv-4.12.0-qt6_py310h5bb264c_603.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libopencv-4.12.0-qt6_py310h3c58159_607.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-2025.2.0-hbf28c98_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-auto-batch-plugin-2025.2.0-hdd9a157_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-auto-plugin-2025.2.0-hdd9a157_1.conda @@ -407,160 +1205,173 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-tensorflow-frontend-2025.2.0-h293fe96_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-tensorflow-lite-frontend-2025.2.0-hac47afa_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopus-1.5.2-h2466b09_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.50-h7351971_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-6.31.1-hdcda5b4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/librsvg-2.58.4-h5ce5fed_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.51-h7351971_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-6.31.1-hdcda5b4_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2025.11.05-h0eb2380_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/librsvg-2.60.0-hd5e4115_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.20-hc70643c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.50.4-hf5d6505_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.0-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libtheora-1.1.1-hc70643c_1006.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.0-h550210a_6.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.1-h8f73337_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libusb-1.0.29-h1839187_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libvorbis-1.3.7-h5112557_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libvulkan-loader-1.4.328.1-h477610d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_9.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.13.8-h741aa76_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h25c3957_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.1-h06f855e_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-ha29bfb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h0fbe4c1_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzip-1.11.2-h3135430_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-20.1.8-hfa2b4ca_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/loguru-0.7.3-pyh7428d3b_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/lxml-6.0.1-py310h4b5876d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lxml-6.0.2-py310h095aac5_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h2466b09_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.8.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.2-py310h38315fa_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.5-py310h5588dad_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.5-py310h0bdd906_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py310hdb0e946_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.8-py310h5588dad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.8-py310h0bdd906_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mergedeep-1.3.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.0.10-h9fa1bad_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mkdocs-1.6.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mkdocs-get-deps-0.2.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/mkdocs-material-9.6.18-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mkdocs-material-9.7.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mkdocs-material-extensions-1.3.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2024.2.2-h57928b3_16.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2024.2.2-h57928b3_15.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mpg123-1.32.9-h01009b0_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.1.1-py310hc19bc0b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.1.2-py310he9f1925_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/multidict-6.6.3-py310hdb0e946_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.4.2-pyh267e887_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-he0c23c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.2.6-py310h4987827_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/open3d-0.19.0-py310hadc9e84_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/open3d-0.19.0-py310hc9aaaf8_6.conda - conda: https://conda.anaconda.org/conda-forge/win-64/opencl-headers-2025.06.13-he0c23c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/opencv-4.12.0-qt6_py310he543868_603.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/openexr-3.3.5-hed76565_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/opencv-4.12.0-qt6_py310he543868_607.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openexr-3.4.4-ha75af49_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openh264-2.6.0-hb17fa0b_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.3-h24db6dd_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.5.2-h725018a_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h24db6dd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openjph-0.25.3-hf13a347_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/paginate-0.5.7-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.3.2-py310hed136d8_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.3.3-py310hed136d8_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.56.4-h03d888a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.45-h99c9b8b_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-11.3.0-py310h6d647b9_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.46-h3402e2f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-11.3.0-py310hb3a2f59_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.3-pyh8b19718_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.4.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.3.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/portmidi-2.0.6-hac47afa_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.6.2-h7990399_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/polars-1.35.2-pyh6a1acc5_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/polars-runtime-32-1.35.2-py310hca7251b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/portmidi-2.0.7-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.7.0-h9080b7b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/propcache-0.3.1-py310h38315fa_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.0.0-py310h29418f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/protobuf-6.31.1-py310h81b7714_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.1.3-py310h1637853_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pugixml-1.15-h372dad0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/py-cpuinfo-9.0.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/py-opencv-4.12.0-qt6_py310h9217539_603.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/py-opencv-4.12.0-qt6_py310h9217539_607.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-4-hd8ed1ab_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pyclean-3.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pygame-2.6.1-py310hea55160_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pymdown-extensions-10.16.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.1-py310h2d19612_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pymdown-extensions-10.17.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.3-py310h96c60bd_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.10.18-h8c5b53a_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.10.19-hc20f281_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.10.19-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.2-py310h38315fa_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py310hdb0e946_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyyaml-env-tag-1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.1-h02ddd7d_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.3-ha0de62e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/rav1e-0.7.1-ha073cba_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-ha104f34_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/retrying-1.4.2-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.7.1-py310h21054b0_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.7.2-py310h21054b0_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.15.2-py310h15c175c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2-2.32.54-he0c23c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2-2.32.56-h5112557_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2_image-2.8.2-h5ca362a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2_mixer-2.6.3-he8f0768_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2_ttf-2.24.0-hd2a6e60_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/sdl3-3.2.20-h5112557_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2_ttf-2.24.0-hb42d5a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/sdl3-3.2.26-h5112557_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.1-py310hb052cae_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/shaderc-2025.4-haa9a63f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.2-py310h62de375_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.50.4-hdb435a2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/statsmodels-0.14.5-py310h8f3aa81_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/spirv-tools-2025.4-h49e36cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.51.0-hdb435a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/statsmodels-0.14.5-py310h8f3aa81_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/svt-av1-3.1.2-hac47afa_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2021.13.0-h18a62a1_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2021.13.0-hd094cb3_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.20.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tensorboard-data-server-0.7.0-py310h5588dad_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tinyobjloader-1.0.7-h63175ca_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.2-py310h29418f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.2-py310h29418f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.22621.0-h57928b3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ultralytics-8.3.186-pyh2a12c56_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-16.0.0-py310ha8f682b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ultralytics-8.3.232-pyh865f8e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.0-py310h29418f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/utfcpp-4.0.6-hc1507ef_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h41ae7f8_31.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_31.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_31.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.44.35208-h38c0c73_31.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vtk-base-9.4.2-py310h27c0482_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/watchdog-6.0.0-py310h5588dad_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/utfcpp-4.0.8-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h2b53caa_32.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_32.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_32.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.44.35208-h38c0c73_32.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vtk-base-9.5.1-py310hcc07014_7.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/watchdog-6.0.0-py310h5588dad_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/win32_setctime-1.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/wslink-2.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wslink-2.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/x264-1!164.3095-h8ffe710_2.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/x265-3.5-h2d74725_3.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-h0e40799_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-h0e40799_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/yarl-1.20.1-py310h38315fa_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/zeromq-4.3.5-ha9f60a1_7.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/yarl-1.22.0-py310hdb0e946_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zeromq-4.3.5-h5bddc39_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.1-h2466b09_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.23.0-py310h29418f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py310h1637853_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-hbeecb71_2.conda - - pypi: https://files.pythonhosted.org/packages/04/eb/f4151e0c7377a6e08a38108609ba5cede57986802757848688aeedd1b9e8/beautifulsoup4-4.13.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/96/fd/a40c621ff207f3ce8e484aa0fc8ba4eb6e3ecf52e15b42ba764b457a9550/editorconfig-0.17.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/2f/e0/014d5d9d7a4564cf1c40b5039bc882db69fd881111e03ab3657ac0b218e2/fsspec-2025.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/14/1c65fccf8413d5f5c6e8425f84675169654395098000d8bddc4e9d3390e1/jsbeautifier-1.15.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/53/b6/106fcc15287e7228658fbd0ad9e8b0d775becced0a089cc39984641f4a0f/mkdocs_git_revision_date_localized_plugin-1.4.7-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/8e/d5/15f6eeeb755e57a501fad6dcfb3fe406dea5f6a6347a77c3be114294f7bb/mkdocs_mermaid2_plugin-1.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/51/fe0e3fdb16f6eed65c9459d12bae6a4e1f0bb4e2228cb037e7907b002678/mkdocs_git_revision_date_localized_plugin-1.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1a/4b/6fd6dd632019b7f522f1b1f794ab6115cd79890330986614be56fd18f0eb/mkdocs_mermaid2_plugin-1.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f0/35/9c524281aab9550187d638862ceb663cb2d31fe04bb630aeb109faf81432/sumolib-1.24.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1a/48/39dbcca267b826eaf7b0edc1f0e78f85698b4132393a894fe58656535927/sumolib-1.25.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl - - pypi: https://download.pytorch.org/whl/nightly/cu128/torch-2.9.0.dev20250827%2Bcu128-cp310-cp310-win_amd64.whl - - pypi: https://download.pytorch.org/whl/nightly/cu128/torchvision-0.24.0.dev20250827%2Bcu128-cp310-cp310-win_amd64.whl + - pypi: https://download.pytorch.org/whl/nightly/cu128/torch-2.10.0.dev20251124%2Bcu128-cp310-cp310-win_amd64.whl + - pypi: https://download.pytorch.org/whl/nightly/cu128/torchvision-0.25.0.dev20251124%2Bcu128-cp310-cp310-win_amd64.whl - pypi: ./dependencies/whl/carla-0.9.15-cp310-cp310-win_amd64.whl packages: - conda: https://conda.anaconda.org/conda-forge/win-64/_libavif_api-1.3.0-h57928b3_2.conda @@ -569,6 +1380,17 @@ packages: purls: [] size: 9734 timestamp: 1756125033129 +- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-7_kmp_llvm.conda + build_number: 7 + sha256: c0cddb66070dd6355311f7667ce2acccf70d1013edaa6e97f22859502fefdb22 + md5: 887b70e1d607fba7957aa02f9ee0d939 + depends: + - llvm-openmp >=9.0.1 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 8244 + timestamp: 1764092331208 - conda: https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-2_gnu.conda build_number: 8 sha256: 1a62cd1f215fe0902e7004089693a78347a30ad687781dfda2289cab000e652d @@ -584,6 +1406,28 @@ packages: purls: [] size: 49468 timestamp: 1718213032772 +- conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + sha256: a3967b937b9abf0f2a99f3173fa4630293979bd1644709d89580e7c62a544661 + md5: aaa2a381ccc56eac91d63b6c1240312f + depends: + - cpython + - python-gil + license: MIT + license_family: MIT + purls: [] + size: 8191 + timestamp: 1744137672556 +- conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.1-pyhd8ed1ab_0.conda + sha256: ec7a804be25350c310be7e0fffdbf4006fd22a650bf316513bdd71cb922944bf + md5: 7d4f1ddc43d323c916b2c744835eb093 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/absl-py?source=hash-mapping + size: 109408 + timestamp: 1751547635237 - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda sha256: 7842ddc678e77868ba7b92a726b437575b23aaec293bca0d40826f1026d90e27 md5: 18fd895e0e775622906cdabfc3cf0fb4 @@ -595,9 +1439,31 @@ packages: - pkg:pypi/aiohappyeyeballs?source=hash-mapping size: 19750 timestamp: 1741775303303 -- conda: https://conda.anaconda.org/conda-forge/win-64/aiohttp-3.12.15-py310hdb0e946_0.conda - sha256: 42334923ac6fc93812029504b82ad42fd36953d27403574221cab371b78b2ef7 - md5: f8be2f1f5aa281b7a1b9d64abe7e7787 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.13.2-py310h5541b7b_0.conda + sha256: 6fa2a52af9f933d557664227f629d3154b3822a79e25933d3a5dc0cfd406938b + md5: 3c3179776c3de575a0c18f88d6614e77 + depends: + - __glibc >=2.17,<3.0.a0 + - aiohappyeyeballs >=2.5.0 + - aiosignal >=1.4.0 + - async-timeout >=4.0,<6.0 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - libgcc >=14 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/aiohttp?source=hash-mapping + size: 884628 + timestamp: 1761727348904 +- conda: https://conda.anaconda.org/conda-forge/win-64/aiohttp-3.13.2-py310h535af70_0.conda + sha256: 54302130e488c06fd84944bae0d6600e550670e1b3099c59a1deffc083589d54 + md5: 847b1ff21a94a6cfdb3b863e638dc8ab depends: - aiohappyeyeballs >=2.5.0 - aiosignal >=1.4.0 @@ -616,8 +1482,8 @@ packages: license_family: Apache purls: - pkg:pypi/aiohttp?source=hash-mapping - size: 827260 - timestamp: 1753805444660 + size: 834729 + timestamp: 1761726844366 - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda sha256: 8dc149a6828d19bf104ea96382a9d04dae185d4a03cc6beb1bc7b84c428e3ca2 md5: 421a865222cd0c9d83ff08bc78bf3a61 @@ -631,6 +1497,17 @@ packages: - pkg:pypi/aiosignal?source=hash-mapping size: 13688 timestamp: 1751626573984 +- conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.14-hb9d3cd8_0.conda + sha256: b9214bc17e89bf2b691fad50d952b7f029f6148f4ac4fe7c60c08f093efdf745 + md5: 76df83c2a9035c54df5d04ff81bcc02d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + license_family: GPL + purls: [] + size: 566531 + timestamp: 1744668655747 - conda: https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 sha256: b91f8ab4ac2b48972fbee1fc8e092cc452fdf59156e4ff2322c94bbf73650f94 md5: c88eaec8de9ae1fa161205aa18e7a5b1 @@ -642,6 +1519,17 @@ packages: - pkg:pypi/antlr4-python3-runtime?source=hash-mapping size: 101065 timestamp: 1638309284042 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + sha256: b08ef033817b5f9f76ce62dfcac7694e7b6b4006420372de22494503decac855 + md5: 346722a0be40f6edc53f12640d301338 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 2706396 + timestamp: 1718551242397 - conda: https://conda.anaconda.org/conda-forge/win-64/aom-3.9.1-he0c23c2_0.conda sha256: 0524d0c0b61dacd0c22ac7a8067f977b1d52380210933b04141f5099c5b6fec7 md5: 3d7c14285d3eb3239a76ff79063f27a5 @@ -654,6 +1542,21 @@ packages: purls: [] size: 1958151 timestamp: 1718551737234 +- conda: https://conda.anaconda.org/conda-forge/linux-64/assimp-5.4.3-hecf2907_1.conda + sha256: c49e992c1a2978f5a8cdf3cdf9aac0c0a444bbddb7316dbfbf16f5a94ff71c10 + md5: 649115e82c2a9620fcf0d3a846ee365f + depends: + - __glibc >=2.17,<3.0.a0 + - libboost >=1.88.0,<1.89.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - zlib + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3645199 + timestamp: 1753274588181 - conda: https://conda.anaconda.org/conda-forge/win-64/assimp-5.4.3-hfc39600_1.conda sha256: 626378a7ba1be595288b8ff3110d7ba1039e059782085b27a79a1904571a778c md5: f1255cae1bcc6600b89f5381e26306b6 @@ -680,17 +1583,28 @@ packages: - pkg:pypi/async-timeout?source=hash-mapping size: 11763 timestamp: 1733235428203 -- conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.3.0-pyh71513ae_0.conda - sha256: 99c53ffbcb5dc58084faf18587b215f9ac8ced36bbfb55fa807c00967e419019 - md5: a10d11958cadc13fdb43df75f8b1903f +- conda: https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.2-h39aace5_0.conda + sha256: a9c114cbfeda42a226e2db1809a538929d2f118ef855372293bd188f71711c48 + md5: 791365c5f65975051e4e017b5da3abf5 depends: - - python >=3.9 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 68072 + timestamp: 1756738968573 +- conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda + sha256: f6c3c19fa599a1a856a88db166c318b148cac3ee4851a9905ed8a04eeec79f45 + md5: c7944d55af26b6d2d7629e27e9a972c1 + depends: + - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/attrs?source=hash-mapping - size: 57181 - timestamp: 1741918625732 + size: 60101 + timestamp: 1759762331492 - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda sha256: 1c656a35800b7f57f7371605bc6507c8d3ad60fbaaec65876fce7f73df1fc8ac md5: 0a01c169f0ab0f91b26e77a3301fbfe4 @@ -714,10 +1628,10 @@ packages: - pkg:pypi/backrefs?source=hash-mapping size: 143810 timestamp: 1740887689966 -- pypi: https://files.pythonhosted.org/packages/04/eb/f4151e0c7377a6e08a38108609ba5cede57986802757848688aeedd1b9e8/beautifulsoup4-4.13.5-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl name: beautifulsoup4 - version: 4.13.5 - sha256: 642085eaa22233aceadff9c69651bc51e8bf3f874fb6d7104ece2beb24b47c4a + version: 4.14.2 + sha256: 5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515 requires_dist: - soupsieve>1.2 - typing-extensions>=4.0.0 @@ -738,6 +1652,22 @@ packages: - pkg:pypi/blinker?source=hash-mapping size: 13934 timestamp: 1731096548765 +- conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda + sha256: e7af5d1183b06a206192ff440e08db1c4e8b2ca1f8376ee45fb2f3a85d4ee45d + md5: 2c2fae981fd2afd00812c92ac47d023d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 48427 + timestamp: 1733513201413 - conda: https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda sha256: 9303a7a0e03cf118eab3691013f6d6cbd1cbac66efbc70d89b20f5d0145257c0 md5: 357d7be4146d5fec543bfaa96a8a40de @@ -754,73 +1684,160 @@ packages: purls: [] size: 49840 timestamp: 1733513605730 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.1.0-h2466b09_3.conda - sha256: d57cd6ea705c9d2a8a2721f083de247501337e459f5498726b564cfca138e192 - md5: c2a23d8a8986c72148c63bdf855ac99a +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + sha256: e511644d691f05eb12ebe1e971fd6dc3ae55a4df5c253b4e1788b789bdf2dfa6 + md5: 8ccf913aaba749a5496c17629d859ed1 + depends: + - __glibc >=2.17,<3.0.a0 + - brotli-bin 1.2.0 hb03c661_1 + - libbrotlidec 1.2.0 hb03c661_1 + - libbrotlienc 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 20103 + timestamp: 1764017231353 +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-h2d644bc_1.conda + sha256: a4fffdf1c9b9d3d0d787e20c724cff3a284dfa3773f9ce609c93b1cfd0ce8933 + md5: bc58fdbced45bb096364de0fba1637af depends: - - brotli-bin 1.1.0 h2466b09_3 - - libbrotlidec 1.1.0 h2466b09_3 - - libbrotlienc 1.1.0 h2466b09_3 + - brotli-bin 1.2.0 hfd05255_1 + - libbrotlidec 1.2.0 hfd05255_1 + - libbrotlienc 1.2.0 hfd05255_1 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + purls: [] + size: 20342 + timestamp: 1764017988883 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + sha256: 64b137f30b83b1dd61db6c946ae7511657eead59fdf74e84ef0ded219605aa94 + md5: af39b9a8711d4a8d437b52c1d78eb6a1 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlidec 1.2.0 hb03c661_1 + - libbrotlienc 1.2.0 hb03c661_1 + - libgcc >=14 license: MIT license_family: MIT purls: [] - size: 20233 - timestamp: 1749230982687 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.1.0-h2466b09_3.conda - sha256: 85aac1c50a426be6d0cc9fd52480911d752f4082cb78accfdb257243e572c7eb - md5: c7c345559c1ac25eede6dccb7b931202 + size: 21021 + timestamp: 1764017221344 +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hfd05255_1.conda + sha256: e76966232ef9612de33c2087e3c92c2dc42ea5f300050735a3c646f33bce0429 + md5: 6abd7089eb3f0c790235fe469558d190 depends: - - libbrotlidec 1.1.0 h2466b09_3 - - libbrotlienc 1.1.0 h2466b09_3 + - libbrotlidec 1.2.0 hfd05255_1 + - libbrotlienc 1.2.0 hfd05255_1 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: MIT license_family: MIT purls: [] - size: 21405 - timestamp: 1749230949991 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.1.0-py310h9e98ed7_3.conda - sha256: 6eac109d40bd36d158064a552babc3da069662ad93712453eb43320f330b7c82 - md5: 52d37d0f3a9286d295fbf72cf0aa99ee + size: 22714 + timestamp: 1764017952449 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py310hba01987_1.conda + sha256: f036fe554d902549f86689a9650a0996901d5c9242b0a1e3fbfe6dbccd2ae011 + md5: 393fca4557fbd2c4d995dcb89f569048 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + constrains: + - libbrotlicommon 1.2.0 hb03c661_1 + license: MIT + license_family: MIT + purls: + - pkg:pypi/brotli?source=hash-mapping + size: 367099 + timestamp: 1764017439384 +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py310hfff998d_1.conda + sha256: fd250a4f92c2176f23dd4e07de1faf76741dabcc8fa00b182748db4d9578ff7e + md5: 0caf12fa6690b7f64883b2239853dda0 depends: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 constrains: - - libbrotlicommon 1.1.0 h2466b09_3 + - libbrotlicommon 1.2.0 hfd05255_1 license: MIT license_family: MIT purls: - pkg:pypi/brotli?source=hash-mapping - size: 321491 - timestamp: 1749231194190 -- conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h2466b09_7.conda - sha256: 35a5dad92e88fdd7fc405e864ec239486f4f31eec229e31686e61a140a8e573b - md5: 276e7ffe9ffe39688abc665ef0f45596 + size: 335476 + timestamp: 1764018212429 +- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda + sha256: c30daba32ddebbb7ded490f0e371eae90f51e72db620554089103b4a6934b0d5 + md5: 51a19bba1b8ebfb60df25cde030b7ebc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 260341 + timestamp: 1757437258798 +- conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda + sha256: d882712855624641f48aa9dc3f5feea2ed6b4e6004585d3616386a18186fe692 + md5: 1077e9333c41ff0be8edd1a5ec0ddace depends: - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: bzip2-1.0.6 license_family: BSD purls: [] - size: 54927 - timestamp: 1720974860185 -- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.8.3-h4c7d964_0.conda - sha256: 3b82f62baad3fd33827b01b0426e8203a2786c8f452f633740868296bcbe8485 - md5: c9e0c0f82f6e63323827db462b40ede8 + size: 55977 + timestamp: 1757437738856 +- conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda + sha256: f8003bef369f57396593ccd03d08a8e21966157269426f71e943f96e4b579aeb + md5: f7f0d6cc2dc986d42ac2689ec88192be + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 206884 + timestamp: 1744127994291 +- conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.5-h2466b09_0.conda + sha256: b52214a0a5632a12587d8dac6323f715bcc890f884efba5a2ce01c48c64ec6dc + md5: b1f84168da1f0b76857df7e5817947a9 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + license: MIT + license_family: MIT + purls: [] + size: 194147 + timestamp: 1744128507613 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-h4c7d964_0.conda + sha256: 686a13bd2d4024fc99a22c1e0e68a7356af3ed3304a8d3ff6bb56249ad4e82f0 + md5: f98fb7db808b94bc1ec5b0e62f9f1069 depends: - __win license: ISC purls: [] - size: 154489 - timestamp: 1754210967212 + size: 152827 + timestamp: 1762967310929 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda + sha256: b986ba796d42c9d3265602bc038f6f5264095702dd546c14bc684e60c385e773 + md5: f0991f0f84902f6b6009b4d2350a83aa + depends: + - __unix + license: ISC + purls: [] + size: 152432 + timestamp: 1762967197890 - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 noarch: python sha256: 561e6660f26c35d137ee150187d89767c988413c978e1b712d53f27ddf70ea17 @@ -843,6 +1860,32 @@ packages: - pkg:pypi/cached-property?source=hash-mapping size: 11065 timestamp: 1615209567874 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda + sha256: 3bd6a391ad60e471de76c0e9db34986c4b5058587fbf2efa5a7f54645e28c2c7 + md5: 09262e66b19567aff4f592fb53b28760 + depends: + - __glibc >=2.17,<3.0.a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - freetype >=2.12.1,<3.0a0 + - icu >=75.1,<76.0a0 + - libexpat >=2.6.4,<3.0a0 + - libgcc >=13 + - libglib >=2.82.2,<3.0a0 + - libpng >=1.6.47,<1.7.0a0 + - libstdcxx >=13 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pixman >=0.44.2,<1.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.5,<2.0a0 + - xorg-libx11 >=1.8.11,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrender >=0.9.12,<0.10.0a0 + license: LGPL-2.1-only or MPL-1.1 + purls: [] + size: 978114 + timestamp: 1741554591855 - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h5782bbf_0.conda sha256: b9f577bddb033dba4533e851853924bfe7b7c1623d0697df382eef177308a917 md5: 20e32ced54300292aff690a69c5e7b97 @@ -863,50 +1906,109 @@ packages: purls: [] size: 1524254 timestamp: 1741555212198 +- pypi: ./dependencies/whl/carla-0.9.15-cp310-cp310-manylinux_2_27_x86_64.whl + name: carla + version: 0.9.15 - pypi: ./dependencies/whl/carla-0.9.15-cp310-cp310-win_amd64.whl name: carla version: 0.9.15 -- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.8.3-pyhd8ed1ab_0.conda - sha256: a1ad5b0a2a242f439608f22a538d2175cac4444b7b3f4e2b8c090ac337aaea40 - md5: 11f59985f49df4620890f3e746ed7102 +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda + sha256: 083a2bdad892ccf02b352ecab38ee86c3e610ba9a4b11b073ea769d55a115d32 + md5: 96a02a5c1a65470a7e4eedb644c872fd depends: - - python >=3.9 + - python >=3.10 license: ISC purls: - pkg:pypi/certifi?source=compressed-mapping - size: 158692 - timestamp: 1754231530168 -- conda: https://conda.anaconda.org/conda-forge/win-64/cffi-1.17.1-py310ha8f682b_0.conda - sha256: 32638e79658f76e3700f783c519025290110f207833ae1d166d262572cbec8a8 - md5: 9c7ec967f4ae263aec56cff05bdbfc07 + size: 157131 + timestamp: 1762976260320 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py310he7384ee_1.conda + sha256: bf76ead6d59b70f3e901476a73880ac92011be63b151972d135eec55bbbe6091 + md5: 803e2d778b8dcccdc014127ec5001681 + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - pycparser + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping + size: 244766 + timestamp: 1761203011221 +- conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py310h29418f3_1.conda + sha256: abd04b75ee9a04a2f00dc102b4dc126f393fde58536ca4eaf1a72bb7d60dadf4 + md5: 269ba3d69bf6569296a29425a26400df depends: - pycparser - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: MIT license_family: MIT purls: - pkg:pypi/cffi?source=hash-mapping - size: 238887 - timestamp: 1725561032032 -- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.3-pyhd8ed1ab_0.conda - sha256: 838d5a011f0e7422be6427becba3de743c78f3874ad2743c341accbba9bb2624 - md5: 7e7d5ef1b9ed630e4a1c358d6bc62284 + size: 239862 + timestamp: 1761203282977 +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda + sha256: b32f8362e885f1b8417bac2b3da4db7323faa12d5db62b7fd6691c02d60d6f59 + md5: a22d1fd9bf98827e280a02875d9a007a depends: - - python >=3.9 + - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/charset-normalizer?source=hash-mapping - size: 51033 - timestamp: 1754767444665 -- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.2.1-pyh7428d3b_0.conda - sha256: 20c2d8ea3d800485245b586a28985cba281dd6761113a49d7576f6db92a0a891 - md5: 3a59475037bc09da916e4062c5cad771 - depends: + size: 50965 + timestamp: 1760437331772 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cli11-2.6.0-h54a6638_0.conda + sha256: 324097cd9dde3a4623b0275655ea34641481daa5c1274f9ab994e8a2e6aa26e6 + md5: ddf9fed4661bace13f33f08fe38a5f45 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 98102 + timestamp: 1760975548381 +- conda: https://conda.anaconda.org/conda-forge/win-64/cli11-2.6.0-h5112557_0.conda + sha256: c8ac28fe221f63e8ed54c68536eef7e822d1820f72ba5d3124eb67aa4ac86c25 + md5: ec1986611c2a48960eab7a8922bf8dcc + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 95629 + timestamp: 1760975549772 +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh707e725_0.conda + sha256: 970b12fb186c3451eee9dd0f10235aeb75fb570b0e9dc83250673c2f0b196265 + md5: 9ba00b39e03a0afb2b1cc0767d4c6175 + depends: + - __unix + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/click?source=compressed-mapping + size: 92604 + timestamp: 1763248639281 +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh7428d3b_0.conda + sha256: 96b83dcb5d6914f5d66367e8d8e96e6e36cf8f0325a75137a3038af070f2d595 + md5: 26ba5c13d304249a96d0852a9138aac6 + depends: - __win - colorama - python >=3.10 @@ -914,8 +2016,8 @@ packages: license_family: BSD purls: - pkg:pypi/click?source=hash-mapping - size: 88117 - timestamp: 1747811467132 + size: 92907 + timestamp: 1763248722090 - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 md5: 962b9857ee8e7018c22f2776ffa0b2d7 @@ -927,6 +2029,22 @@ packages: - pkg:pypi/colorama?source=hash-mapping size: 27011 timestamp: 1733218222191 +- conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.2-py310h3788b33_0.conda + sha256: 5231c1b68e01a9bc9debabc077a6fb48c4395206d59f40a4598d1d5e353e11d8 + md5: b6420d29123c7c823de168f49ccdfe6a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - numpy >=1.23 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/contourpy?source=hash-mapping + size: 261280 + timestamp: 1744743236964 - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.2-py310hc19bc0b_0.conda sha256: 096a7cf6bf77faf3e093936d831118151781ddbd2ab514355ee2f0104b490b1e md5: 039416813b5290e7d100a05bb4326110 @@ -943,6 +2061,17 @@ packages: - pkg:pypi/contourpy?source=hash-mapping size: 201075 timestamp: 1744743764641 +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.10.19-py310hd8ed1ab_2.conda + noarch: generic + sha256: a6e5a75a2a79d7bb2519930b8e897ee062983d58af97d82050698e3df4b3b100 + md5: 5aad50a3b1adaffbc9713a2a8dd87bfc + depends: + - python >=3.10,<3.11.0a0 + - python_abi * *_cp310 + license: Python-2.0 + purls: [] + size: 50196 + timestamp: 1761172103204 - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda sha256: 9827efa891e507a91a8a2acf64e210d2aff394e1cde432ad08e1f8c66b12293c md5: 44600c4667a319d67dbe0681fc0bc833 @@ -954,15 +2083,31 @@ packages: - pkg:pypi/cycler?source=hash-mapping size: 13399 timestamp: 1733332563512 -- conda: https://conda.anaconda.org/conda-forge/noarch/dash-3.2.0-pyhd8ed1ab_0.conda - sha256: c0236a03ec31300ef339d8bd7a5cc3bc0828245d0e29de6eb0bb5abf12990d69 - md5: da955d1c354e25b15b4e09f837baf01d +- conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda + sha256: ee09ad7610c12c7008262d713416d0b58bf365bc38584dce48950025850bdf3f + md5: cae723309a49399d2949362f4ab5c9e4 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libntlm >=1.8,<2.0a0 + - libstdcxx >=13 + - libxcrypt >=4.4.36 + - openssl >=3.5.0,<4.0a0 + license: BSD-3-Clause-Attribution + license_family: BSD + purls: [] + size: 209774 + timestamp: 1750239039316 +- conda: https://conda.anaconda.org/conda-forge/noarch/dash-3.3.0-pyhd8ed1ab_0.conda + sha256: a8e6b531b5fb9582ce2087be74ebd57b095e9db4b1d25d03106b3259fe3259c5 + md5: 1e4dab5c51cef84cf867108cddb09835 depends: - flask >=1.0.4 - importlib-metadata - nest-asyncio - plotly >=5.0.0 - - python >=3.9 + - python >=3.10 - requests - retrying - setuptools @@ -970,14 +2115,24 @@ packages: - werkzeug constrains: - dash-html-components >=2.0.0 - - dash_table >=5.0.0 - dash-core-components >=2.0.0 + - dash_table >=5.0.0 license: MIT license_family: MIT purls: - pkg:pypi/dash?source=hash-mapping - size: 5583788 - timestamp: 1754906535723 + size: 5787240 + timestamp: 1763190015818 +- conda: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + sha256: 22053a5842ca8ee1cf8e1a817138cdb5e647eb2c46979f84153f6ad7bde73020 + md5: 418c6ca5929a611cbd69204907a83995 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 760229 + timestamp: 1685695754230 - conda: https://conda.anaconda.org/conda-forge/win-64/dav1d-1.2.1-hcfcfb64_0.conda sha256: 2aa2083c9c186da7d6f975ccfbef654ed54fff27f4bc321dbcd12cee932ec2c4 md5: ed2c27bda330e3f0ab41577cf8b9b585 @@ -990,6 +2145,34 @@ packages: purls: [] size: 618643 timestamp: 1685696352968 +- conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda + sha256: 3b988146a50e165f0fa4e839545c679af88e4782ec284cc7b6d07dd226d6a068 + md5: 679616eb5ad4e521c83da4650860aba7 + depends: + - libstdcxx >=13 + - libgcc >=13 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libexpat >=2.7.0,<3.0a0 + - libzlib >=1.3.1,<2.0a0 + - libglib >=2.84.2,<3.0a0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 437860 + timestamp: 1747855126005 +- conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda + sha256: 1bcc132fbcc13f9ad69da7aa87f60ea41de7ed4d09f3a00ff6e0e70e1c690bc2 + md5: bfd56492d8346d669010eccafe0ba058 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 69544 + timestamp: 1739569648873 - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.3.1-he0c23c2_0.conda sha256: b1fee32ef36a98159f0a2a96c4e734dfc9adff73acd444940831b22c1fb6d5c0 md5: e9a1402439c18a4e3c7a52e4246e9e1c @@ -1009,6 +2192,19 @@ packages: requires_dist: - mypy>=1.15 ; extra == 'dev' requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/embree-4.4.0-ha751380_1.conda + sha256: aabfa509404b2e3df9aa28b2304465822d709761fdac0b0a262b07eb6a1b425e + md5: 69603afa88336a2c836721dc980b742e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - tbb >=2022.3.0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 9506863 + timestamp: 1763127235152 - conda: https://conda.anaconda.org/conda-forge/win-64/embree-4.4.0-ha827246_0.conda sha256: e6e26f961834aa70aeca9706d19ca21979477462c24c782bb9bd57b665948b82 md5: e3acdfb0f456a92bf4bee819c072734e @@ -1022,30 +2218,95 @@ packages: purls: [] size: 7243408 timestamp: 1743780575030 -- conda: https://conda.anaconda.org/conda-forge/win-64/ffmpeg-7.1.1-gpl_h70aa942_909.conda - sha256: f189f022aca4a1e99e312386b587da0000d699b4941a5d3fe0019e832a00abe0 - md5: a15f25488d4b26d4e20d05d6f3700c1f +- conda: https://conda.anaconda.org/conda-forge/linux-64/ffmpeg-8.0.0-gpl_hbbdf940_906.conda + sha256: 7a62272c6f12e8074d9e6b3e7423c906ed9ee5bb0a66c1256ad341449308e158 + md5: e345ae3d8bc570a3f6ea89f03ff1110a depends: + - __glibc >=2.17,<3.0.a0 + - alsa-lib >=1.2.14,<1.3.0a0 - aom >=3.9.1,<3.10.0a0 - bzip2 >=1.0.8,<2.0a0 - dav1d >=1.2.1,<1.2.2.0a0 - fontconfig >=2.15.0,<3.0a0 - fonts-conda-ecosystem - - harfbuzz >=11.4.3 + - gmp >=6.3.0,<7.0a0 + - harfbuzz >=11.5.1 - lame >=3.100,<3.101.0a0 + - libass >=0.17.4,<0.17.5.0a0 - libexpat >=2.7.1,<3.0a0 - - libfreetype >=2.13.3 - - libfreetype6 >=2.13.3 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.1,<6.0a0 + - libopenvino >=2025.2.0,<2025.2.1.0a0 + - libopenvino-auto-batch-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-auto-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-hetero-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-intel-cpu-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-intel-gpu-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-intel-npu-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-ir-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-onnx-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-paddle-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-pytorch-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-tensorflow-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-tensorflow-lite-frontend >=2025.2.0,<2025.2.1.0a0 - libopus >=1.5.2,<2.0a0 - librsvg >=2.58.4,<3.0a0 + - libstdcxx >=14 + - libva >=2.22.0,<3.0a0 - libvorbis >=1.3.7,<1.4.0a0 - - libxml2 >=2.13.8,<2.14.0a0 + - libvpl >=2.15.0,<2.16.0a0 + - libvpx >=1.14.1,<1.15.0a0 + - libvulkan-loader >=1.4.313.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 - libzlib >=1.3.1,<2.0a0 - openh264 >=2.6.0,<2.6.1.0a0 - - openssl >=3.5.2,<4.0a0 - - sdl2 >=2.32.54,<3.0a0 + - openssl >=3.5.3,<4.0a0 + - pulseaudio-client >=17.0,<17.1.0a0 + - sdl2 >=2.32.56,<3.0a0 + - shaderc >=2025.4,<2025.5.0a0 + - svt-av1 >=3.1.2,<3.1.3.0a0 + - x264 >=1!164.3095,<1!165 + - x265 >=3.5,<3.6.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + constrains: + - __cuda >=12.8 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 12445427 + timestamp: 1758924965297 +- conda: https://conda.anaconda.org/conda-forge/win-64/ffmpeg-8.0.0-gpl_he3062b8_906.conda + sha256: c35f51336ae9ccc4f30b85559537d3dea3208b8baf602a051ad8679872783a76 + md5: cc00f155fcff8ffba98f8a44304dd229 + depends: + - aom >=3.9.1,<3.10.0a0 + - bzip2 >=1.0.8,<2.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - harfbuzz >=11.5.1 + - lame >=3.100,<3.101.0a0 + - libexpat >=2.7.1,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libopus >=1.5.2,<2.0a0 + - librsvg >=2.58.4,<3.0a0 + - libvorbis >=1.3.7,<1.4.0a0 + - libvulkan-loader >=1.4.313.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.1,<2.0a0 + - openh264 >=2.6.0,<2.6.1.0a0 + - openssl >=3.5.3,<4.0a0 + - sdl2 >=2.32.56,<3.0a0 + - shaderc >=2025.4,<2025.5.0a0 - svt-av1 >=3.1.2,<3.1.3.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 @@ -1057,13 +2318,13 @@ packages: license: GPL-2.0-or-later license_family: GPL purls: [] - size: 10054867 - timestamp: 1756130360695 -- pypi: https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl + size: 10429729 + timestamp: 1758926477654 +- pypi: https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl name: filelock - version: 3.19.1 - sha256: d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d - requires_python: '>=3.9' + version: 3.20.0 + sha256: 339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2 + requires_python: '>=3.10' - conda: https://conda.anaconda.org/conda-forge/noarch/flask-3.1.2-pyhd8ed1ab_0.conda sha256: 8a97eba37e0723720706d4636cc89c6b07eea1b7cc66fd8994fa8983a81ed988 md5: ba67a9febeda36948fee26a3dec3d914 @@ -1082,6 +2343,38 @@ packages: - pkg:pypi/flask?source=hash-mapping size: 82438 timestamp: 1755674743256 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fluidsynth-2.3.7-hd992666_0.conda + sha256: 0bf26d25ae79e6f5f01a49a00e9ba3b60b10dd4c12ec43bdba51055c26bc9dd6 + md5: dd6c7b8a1b217ef7522ca987c465651d + depends: + - __glibc >=2.17,<3.0.a0 + - alsa-lib >=1.2.12,<1.3.0a0 + - jack >=1.9.22,<1.10.0a0 + - libgcc >=13 + - libglib >=2.82.2,<3.0a0 + - libsndfile >=1.2.2,<1.3.0a0 + - libstdcxx >=13 + - portaudio >=19.6.0,<19.7.0a0 + - pulseaudio-client >=17.0,<17.1.0a0 + - readline >=8.2,<9.0a0 + - sdl2 >=2.30.7,<3.0a0 + license: GPL-2.0-or-later + license_family: LGPL + purls: [] + size: 279996 + timestamp: 1729590344462 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-11.2.0-h07f6e7f_0.conda + sha256: e0f53b7801d0bcb5d61a1ddcb873479bfe8365e56fd3722a232fbcc372a9ac52 + md5: 0c2f855a88fab6afa92a7aa41217dc8e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + purls: [] + size: 192721 + timestamp: 1751277120358 - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-11.2.0-h1d4551f_0.conda sha256: 890f2789e55b509ff1f14592a5b20a0d0ec19f6da463eff96e378a5d70f882da md5: 15b63c3fb5b7d67b1cb63553a33e6090 @@ -1126,6 +2419,21 @@ packages: purls: [] size: 1620504 timestamp: 1727511233259 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda + sha256: 7093aa19d6df5ccb6ca50329ef8510c6acb6b0d8001191909397368b65b02113 + md5: 8f5b0b297b59e1ac160ad4beec99dbee + depends: + - __glibc >=2.17,<3.0.a0 + - freetype >=2.12.1,<3.0a0 + - libexpat >=2.6.3,<3.0a0 + - libgcc >=13 + - libuuid >=2.38.1,<3.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 265599 + timestamp: 1730283881107 - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.15.0-h765892d_1.conda sha256: ed122fc858fb95768ca9ca77e73c8d9ddc21d4b2e13aaab5281e27593e840691 md5: 9bb0026a2131b09404c59c4290c697cd @@ -1152,22 +2460,39 @@ packages: purls: [] size: 3667 timestamp: 1566974674465 -- conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2 - sha256: 53f23a3319466053818540bcdf2091f253cbdbab1e0e9ae7b9e509dcaa2a5e38 - md5: f766549260d6815b0c52253f1fb1bb29 +- conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + sha256: 54eea8469786bc2291cc40bca5f46438d3e062a399e8f53f013b6a9f50e98333 + md5: a7970cd949a077b7cb9696379d338681 depends: - - font-ttf-dejavu-sans-mono + - font-ttf-ubuntu - font-ttf-inconsolata + - font-ttf-dejavu-sans-mono - font-ttf-source-code-pro - - font-ttf-ubuntu license: BSD-3-Clause license_family: BSD purls: [] - size: 4102 - timestamp: 1566932280397 -- conda: https://conda.anaconda.org/conda-forge/win-64/fonttools-4.59.2-py310hdb0e946_0.conda - sha256: 93eaf4c063327cb9a47ed383608e34c79329eb1fcc030f4fa5c1d945c7878269 - md5: 2072c4ef8b99bee252d62c4bfbf6c2e6 + size: 4059 + timestamp: 1762351264405 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.60.1-py310h3406613_0.conda + sha256: dc1576438d88ffa4e97012959ad3fb7cc426e6c7eb213eb73815322a42115704 + md5: ac183a1fd0cbebd32a20a2aeaf8dc01d + depends: + - __glibc >=2.17,<3.0.a0 + - brotli + - libgcc >=14 + - munkres + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - unicodedata2 >=15.1.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/fonttools?source=hash-mapping + size: 2424038 + timestamp: 1759187516379 +- conda: https://conda.anaconda.org/conda-forge/win-64/fonttools-4.60.1-py310hdb0e946_0.conda + sha256: a51bc5251ed0c173918ab67371a8a9b1345c8f7acabf5e4d4535be35916c02ec + md5: e8ab7eaefb6b9ea807fbe0b841fda092 depends: - brotli - munkres @@ -1181,8 +2506,25 @@ packages: license_family: MIT purls: - pkg:pypi/fonttools?source=hash-mapping - size: 1978539 - timestamp: 1756329124434 + size: 1995757 + timestamp: 1759187504348 +- conda: https://conda.anaconda.org/conda-forge/linux-64/freeglut-3.2.2-ha6d2627_3.conda + sha256: 676540a8e7f73a894cb1fcb870e7bec623ec1c0a2d277094fd713261a02d8d56 + md5: 84ec3f5b46f3076be49f2cf3f1cfbf02 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - libxcb >=1.16,<2.0.0a0 + - xorg-libx11 >=1.8.9,<2.0a0 + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxext >=1.3.4,<2.0a0 + - xorg-libxfixes + - xorg-libxi + license: MIT + license_family: MIT + purls: [] + size: 144010 + timestamp: 1719014356708 - conda: https://conda.anaconda.org/conda-forge/win-64/freeglut-3.2.2-he0c23c2_3.conda sha256: 8b41913ed6c8c0dadda463a649bc16f45e88faa58553efc6830f4de1138c97f2 md5: 5872031ef7cba8435ff24af056777473 @@ -1195,26 +2537,62 @@ packages: purls: [] size: 111956 timestamp: 1719014753462 -- conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.13.3-h57928b3_1.conda - sha256: 0bcc9c868d769247c12324f957c97c4dbee7e4095485db90d9c295bcb3b1bb43 - md5: 633504fe3f96031192e40e3e6c18ef06 +- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda + sha256: bf8e4dffe46f7d25dc06f31038cacb01672c47b9f45201f065b0f4d00ab0a83e + md5: 4afc585cd97ba8a23809406cd8a9eda8 + depends: + - libfreetype 2.14.1 ha770c72_0 + - libfreetype6 2.14.1 h73754d4_0 + license: GPL-2.0-only OR FTL + purls: [] + size: 173114 + timestamp: 1757945422243 +- conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.1-h57928b3_0.conda + sha256: a9b3313edea0bf14ea6147ea43a1059d0bf78771a1336d2c8282891efc57709a + md5: d69c21967f35eb2ce7f1f85d6b6022d3 depends: - - libfreetype 2.13.3 h57928b3_1 - - libfreetype6 2.13.3 h0b5ce68_1 + - libfreetype 2.14.1 h57928b3_0 + - libfreetype6 2.14.1 hdbac1cb_0 license: GPL-2.0-only OR FTL purls: [] - size: 184162 - timestamp: 1745370242683 -- conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.10-h8d14728_0.tar.bz2 - sha256: e0323e6d7b6047042970812ee810c6b1e1a11a3af4025db26d0965ae5d206104 - md5: 807e81d915f2bb2e49951648615241f6 + size: 184553 + timestamp: 1757946164012 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda + sha256: 858283ff33d4c033f4971bf440cebff217d5552a5222ba994c49be990dacd40d + md5: f9f81ea472684d75b9dd8d0b328cf655 depends: - - vc >=14.1,<15.0a0 - - vs2015_runtime >=14.16.27012 - license: LGPL-2.1 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-or-later + purls: [] + size: 61244 + timestamp: 1757438574066 +- conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_0.conda + sha256: 15011071ee56c216ffe276c8d734427f1f893f275ef733f728d13f610ed89e6e + md5: c27bd87e70f970010c1c6db104b88b18 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: LGPL-2.1-or-later purls: [] - size: 64567 - timestamp: 1604417122064 + size: 64394 + timestamp: 1757438741305 +- conda: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.7.0-py310h9548a50_0.conda + sha256: c8abeb6da1e89113049d01c714fcce67e2fcc2853a63b3c40078372a5f66c59f + md5: 50e2b335c9da85d4eadaab11cf245415 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/frozenlist?source=hash-mapping + size: 54180 + timestamp: 1752167428701 - conda: https://conda.anaconda.org/conda-forge/win-64/frozenlist-1.7.0-py310had1666a_0.conda sha256: f8fef785240277976e41f91beaa342d86e6cc62e47013fa0cf2ff0a1ff506ba7 md5: bdec97148fe85aa11f269fa26a76274c @@ -1230,10 +2608,10 @@ packages: - pkg:pypi/frozenlist?source=hash-mapping size: 49063 timestamp: 1752167874366 -- pypi: https://files.pythonhosted.org/packages/2f/e0/014d5d9d7a4564cf1c40b5039bc882db69fd881111e03ab3657ac0b218e2/fsspec-2025.7.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl name: fsspec - version: 2025.7.0 - sha256: 8b012e39f63c7d5f10474de957f3ab793b47b45ae7d39f2fb735f8bbe25c0e21 + version: 2025.10.0 + sha256: 7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d requires_dist: - adlfs ; extra == 'abfs' - adlfs ; extra == 'adl' @@ -1337,35 +2715,91 @@ packages: - zstandard ; python_full_version < '3.14' and extra == 'test-full' - tqdm ; extra == 'tqdm' requires_python: '>=3.9' -- conda: https://conda.anaconda.org/conda-forge/win-64/gdk-pixbuf-2.42.12-h1f5b9c4_3.conda - sha256: 1276e8d2164701ddf4ff708ac6131e95d9030e11fe0ca2df3657e9a54319ade4 - md5: df24f48f53cd1fdeb9fe8bf6e323c715 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.4-h2b0a6b4_0.conda + sha256: f47222f58839bcc77c15f11a8814c1d8cb8080c5ca6ba83398a12b640fd3c85c + md5: c379d67c686fb83475c1a6ed41cc41ff + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libglib >=2.86.0,<3.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblzma >=5.8.1,<6.0a0 + - libpng >=1.6.50,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + size: 572093 + timestamp: 1761082340749 +- conda: https://conda.anaconda.org/conda-forge/win-64/gdk-pixbuf-2.44.4-h1f5b9c4_0.conda + sha256: 24189e4615a0aa574ab2bd5c270fff999da6951e3cd391f1e807c7e4fafd5cdc + md5: 0ce8e4983a4c60a5b75a9a5b5f227447 depends: - - libglib >=2.84.3,<3.0a0 + - libglib >=2.86.0,<3.0a0 - libintl >=0.22.5,<1.0a0 - libjpeg-turbo >=3.1.0,<4.0a0 - liblzma >=5.8.1,<6.0a0 - libpng >=1.6.50,<1.7.0a0 - - libtiff >=4.7.0,<4.8.0a0 + - libtiff >=4.7.1,<4.8.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LGPL-2.1-or-later license_family: LGPL purls: [] - size: 579008 - timestamp: 1754960318590 -- conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.13.1-h9ea8674_0.conda - sha256: 8b2dd4b831ddac64584d49b50f0547c90f5b352a7ec62f941686bb59c21d6055 - md5: 2ebe8eb886545cdc287324d41186a698 + size: 573466 + timestamp: 1761082560321 +- conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda + sha256: 08896dcd94e14a83f247e91748444e610f344ab42d80cbf2b6082b481c3f8f4b + md5: 4d4efd0645cd556fab54617c4ad477ef + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: LGPL-2.1-only + purls: [] + size: 1974942 + timestamp: 1761593471198 +- conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-hdade9fe_0.conda + sha256: 032a16d78e69a20ffae6216191a66977bc50f6c21cb75f9853b37298b95308c4 + md5: 8c75d7e401a4d799ce8d4bb922320967 depends: - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: LGPL-2.1-only purls: [] - size: 1703268 - timestamp: 1741052039669 + size: 1772787 + timestamp: 1761593910217 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-0.25.1-h3f43e3d_1.conda + sha256: cbfa8c80771d1842c2687f6016c5e200b52d4ca8f2cc119f6377f64f899ba4ff + md5: c42356557d7f2e37676e121515417e3b + depends: + - __glibc >=2.17,<3.0.a0 + - gettext-tools 0.25.1 h3f43e3d_1 + - libasprintf 0.25.1 h3f43e3d_1 + - libasprintf-devel 0.25.1 h3f43e3d_1 + - libgcc >=14 + - libgettextpo 0.25.1 h3f43e3d_1 + - libgettextpo-devel 0.25.1 h3f43e3d_1 + - libiconv >=1.18,<2.0a0 + - libstdcxx >=14 + license: LGPL-2.1-or-later AND GPL-3.0-or-later + purls: [] + size: 541357 + timestamp: 1753343006214 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-tools-0.25.1-h3f43e3d_1.conda + sha256: c792729288bdd94f21f25f80802d4c66957b4e00a57f7cb20513f07aadfaff06 + md5: a59c05d22bdcbb4e984bf0c021a2a02f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 3644103 + timestamp: 1753342966311 - conda: https://conda.anaconda.org/conda-forge/noarch/ghp-import-2.1.0-pyhd8ed1ab_2.conda sha256: 40fdf5a9d5cc7a3503cd0c33e1b90b1e6eab251aaaa74e6b965417d089809a15 md5: 93f742fe078a7b34c29a182958d4d765 @@ -1403,6 +2837,18 @@ packages: - pkg:pypi/gitpython?source=hash-mapping size: 157875 timestamp: 1753444241693 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gl2ps-1.4.2-hae5d5c5_1.conda + sha256: 68f071ea25e79ee427c0d6c35ccc137d66f093a37660a4e41bafe0c49d64f2d6 + md5: 00e642ec191a19bf806a3915800e9524 + depends: + - libgcc-ng >=12 + - libpng >=1.6.43,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + license: LGPL-2.0-or-later + license_family: LGPL + purls: [] + size: 74102 + timestamp: 1718542981099 - conda: https://conda.anaconda.org/conda-forge/win-64/gl2ps-1.4.2-had7236b_1.conda sha256: 5a18f0aa963adb4402dbce93516f40756beaa206e82c56592aafb1eb88060ba5 md5: 033491c5cb1ce4e915238307f0136fa0 @@ -1417,28 +2863,109 @@ packages: purls: [] size: 71943 timestamp: 1718543473790 -- conda: https://conda.anaconda.org/conda-forge/win-64/glew-2.1.0-h39d44d4_2.tar.bz2 - sha256: 6a780b5ca7253129ea5e63671f0aeafc8f119167e170a60ccbd8573669ef848d - md5: 840d21c1ee66b91af3d0211e7766393a +- conda: https://conda.anaconda.org/conda-forge/linux-64/glew-2.2.0-h3abd4de_0.conda + sha256: fe1232f1a00b671091eff53388ef4dffc1e0e0efeb1c3e7c8ee4cbbbda968c80 + md5: 6ea943ca4f0d01d6eec6a60d24415dc5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libglu >=9.0.3,<9.1.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxext + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 544416 + timestamp: 1760370322297 +- conda: https://conda.anaconda.org/conda-forge/win-64/glew-2.2.0-hcc5dbe9_0.conda + sha256: 11c993344d5b3347b766c6aac968402b0c9e2eb6c93beeecd816db61200c40f8 + md5: aa2718b4135598789862051086c223ea depends: - - vc >=14.1,<15.0a0 - - vs2015_runtime >=14.16.27012 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD purls: [] - size: 963275 - timestamp: 1607113700054 -- conda: https://conda.anaconda.org/conda-forge/win-64/glfw-3.5.0-hfd05255_0.conda - sha256: 06a8b334515f93778cdbdfd218a1a68346a5981ed8f3d89c70a5df75b415d310 - md5: fde2211ff131ee221a8ec6c4cb5e5752 + size: 784982 + timestamp: 1760370568105 +- conda: https://conda.anaconda.org/conda-forge/linux-64/glfw-3.4-hb03c661_1.conda + sha256: 8586f786e71a52dc37be6a78c37fd8526b80539082352196f6f7440392ce33d0 + md5: bf1df8613072b03a7fb4b77b9fa54048 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxkbcommon >=1.11.0,<2.0a0 + - wayland >=1.24.0,<2.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - xorg-libxi >=1.8.2,<2.0a0 + - xorg-libxinerama >=1.1.5,<1.2.0a0 + - xorg-libxrandr >=1.5.4,<2.0a0 + license: Zlib + purls: [] + size: 166432 + timestamp: 1758809197097 +- conda: https://conda.anaconda.org/conda-forge/win-64/glfw-3.4-hfd05255_1.conda + sha256: b71b2cdaf2b781608a1b5b3fb772c713032057cb79bc1343d9f30b8f7f65daa1 + md5: 74d8bf762b7fe17e02b586b2d8bc1da3 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: Zlib purls: [] - size: 119783 - timestamp: 1754999626593 + size: 119345 + timestamp: 1758809778618 +- conda: https://conda.anaconda.org/conda-forge/linux-64/glslang-16.0.0-hfd11570_0.conda + sha256: 6f35ba185e93b354297a1af138b2e8ede9b125a55acaa03ff14e00947126fc1b + md5: 8fc94e8de494e675c6ca1426b97ed67a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - spirv-tools >=2025,<2026.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1300234 + timestamp: 1758851979910 +- conda: https://conda.anaconda.org/conda-forge/win-64/glslang-16.0.0-h5b34520_0.conda + sha256: ed064df5a5a0b9ef5d02422237cd53bfab8c6327c5e97f0f780e7bc783a7c982 + md5: 4c5a0555e8a8b573603a8759b82a622b + depends: + - spirv-tools >=2025,<2026.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 4710311 + timestamp: 1758852257517 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hac33072_2.conda + sha256: 309cf4f04fec0c31b6771a5809a1909b4b3154a2208f52351e1ada006f4c750c + md5: c94a5994ef49749880a8139cf9afcbe1 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: GPL-2.0-or-later OR LGPL-3.0-or-later + purls: [] + size: 460055 + timestamp: 1718980856608 +- conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda + sha256: 25ba37da5c39697a77fce2c9a15e48cf0a84f1464ad2aafbe53d8357a9f6cc8c + md5: 2cd94587f3a401ae05e03a6caf09539d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: LGPL-2.0-or-later + license_family: LGPL + purls: [] + size: 99596 + timestamp: 1755102025473 - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.14-hac47afa_2.conda sha256: 5f1714b07252f885a62521b625898326ade6ca25fbc20727cfe9a88f68a54bfd md5: b785694dd3ec77a011ccf0c24725382b @@ -1451,22 +2978,74 @@ packages: purls: [] size: 96336 timestamp: 1755102441729 -- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda - sha256: 0aa1cdc67a9fe75ea95b5644b734a756200d6ec9d0dff66530aec3d1c1e9df75 - md5: b4754fb1bdcb70c8fd54f918301582c6 +- conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.74.1-py310h8f3b6d6_1.conda + sha256: 50f125b89b320f1f19dc35f5fd0840bf92d0ea0115594b9d948542aaac8b3ccd + md5: 60a611264a1bc4592d1a09f76e3ecc85 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgrpc 1.74.1 h3288cfb_1 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/grpcio?source=hash-mapping + size: 831290 + timestamp: 1759629800986 +- conda: https://conda.anaconda.org/conda-forge/win-64/grpcio-1.74.1-py310hc77e67f_1.conda + sha256: f77c1e0f9a97e07f54217c82bdd7771b9338040152d784cf41f8c3d6b804e8dd + md5: 23685ebc59a6f221034c87b48667ff12 + depends: + - libgrpc 1.74.1 h317e13b_1 + - libzlib >=1.3.1,<2.0a0 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/grpcio?source=hash-mapping + size: 669167 + timestamp: 1759616429723 +- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + sha256: 84c64443368f84b600bfecc529a1194a3b14c3656ee2e832d15a20e0329b6da3 + md5: 164fc43f0b53b6e3a7bc7dce5e4f1dc9 depends: - - hpack >=4.1,<5 + - python >=3.10 - hyperframe >=6.1,<7 - - python >=3.9 + - hpack >=4.1,<5 + - python license: MIT license_family: MIT purls: - - pkg:pypi/h2?source=hash-mapping - size: 53888 - timestamp: 1738578623567 -- conda: https://conda.anaconda.org/conda-forge/win-64/h5py-3.14.0-nompi_py310h877c39c_100.conda - sha256: 754155af401cb3577c3e76ed7a4427ad9928c210d32b8571f84492c54b67f5a4 - md5: 5b861086c5a7689a6d95c4df10d211e4 + - pkg:pypi/h2?source=compressed-mapping + size: 95967 + timestamp: 1756364871835 +- conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py310h4aa865e_101.conda + sha256: 427fc2540a4728dc80d9f0b464541aed61d35ae9ccafcd7f6bbce499eeaf8ce9 + md5: 4fccf52eaeb2ae9d9e251623e2b66e63 + depends: + - __glibc >=2.17,<3.0.a0 + - cached-property + - hdf5 >=1.14.6,<1.14.7.0a0 + - libgcc >=14 + - numpy >=1.21,<3 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/h5py?source=hash-mapping + size: 1217205 + timestamp: 1764016763175 +- conda: https://conda.anaconda.org/conda-forge/win-64/h5py-3.15.1-nompi_py310hb7e4da9_101.conda + sha256: cdd286739e413eb96a6b6d304d8ad53cb3345e426a75c4c44ce55d1a1a649efb + md5: 357927e58b9ead286f57328aa6eff36b depends: - cached-property - hdf5 >=1.14.6,<1.14.7.0a0 @@ -1474,36 +3053,70 @@ packages: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/h5py?source=hash-mapping - size: 965919 - timestamp: 1749298835168 -- conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-11.4.4-h5f2951f_0.conda - sha256: 290d27b9a1b63e52995eee47e4507e565c2fe6c52a881b6d9c3d65dc02d851fb - md5: e20c9b1d2e10640d3de889981986dd8a + size: 973478 + timestamp: 1764017076833 +- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.2.0-h15599e2_0.conda + sha256: 6bd8b22beb7d40562b2889dc68232c589ff0d11a5ad3addd41a8570d11f039d9 + md5: b8690f53007e9b5ee2c2178dd4ac778c depends: + - __glibc >=2.17,<3.0.a0 - cairo >=1.18.4,<2.0a0 - graphite2 >=1.3.14,<2.0a0 - icu >=75.1,<76.0a0 - libexpat >=2.7.1,<3.0a0 - - libfreetype >=2.13.3 - - libfreetype6 >=2.13.3 - - libglib >=2.84.3,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libglib >=2.86.1,<3.0a0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 2411408 + timestamp: 1762372726141 +- conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-12.2.0-h5f2951f_0.conda + sha256: db73714c7f7e0c47b3b9db9302a83f2deb6f8d6081716d35710ef3c6756af6c3 + md5: e798ef748fc564e42f381d3d276850f0 + depends: + - cairo >=1.18.4,<2.0a0 + - graphite2 >=1.3.14,<2.0a0 + - icu >=75.1,<76.0a0 + - libexpat >=2.7.1,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libglib >=2.86.1,<3.0a0 - libzlib >=1.3.1,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: MIT + license_family: MIT purls: [] - size: 1135658 - timestamp: 1756304491764 -- conda: https://conda.anaconda.org/conda-forge/win-64/hdf4-4.2.15-h5557f11_7.conda - sha256: 52fa5dde69758c19c69ab68a3d7ebfb2c9042e3a55d405c29a59d3b0584fd790 - md5: 84344a916a73727c1326841007b52ca8 + size: 1138900 + timestamp: 1762373626704 +- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda + sha256: 0d09b6dc1ce5c4005ae1c6a19dc10767932ef9a5e9c755cfdbb5189ac8fb0684 + md5: bd77f8da987968ec3927990495dc22e4 + depends: + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libstdcxx-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 756742 + timestamp: 1695661547874 +- conda: https://conda.anaconda.org/conda-forge/win-64/hdf4-4.2.15-h5557f11_7.conda + sha256: 52fa5dde69758c19c69ab68a3d7ebfb2c9042e3a55d405c29a59d3b0584fd790 + md5: 84344a916a73727c1326841007b52ca8 depends: - libjpeg-turbo >=3.0.0,<4.0a0 - libzlib >=1.2.13,<2.0.0a0 @@ -1515,6 +3128,24 @@ packages: purls: [] size: 779637 timestamp: 1695662145568 +- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda + sha256: 4f173af9e2299de7eee1af3d79e851bca28ee71e7426b377e841648b51d48614 + md5: c74d83614aec66227ae5199d98852aaf + depends: + - __glibc >=2.17,<3.0.a0 + - libaec >=1.1.4,<2.0a0 + - libcurl >=8.14.1,<9.0a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3710057 + timestamp: 1753357500665 - conda: https://conda.anaconda.org/conda-forge/win-64/hdf5-1.14.6-nompi_he30205f_103.conda sha256: 0a90263b97e9860cec6c2540160ff1a1fff2a609b3d96452f8716ae63489dac5 md5: f1f7aaf642cefd2190582550eaca4658 @@ -1553,6 +3184,18 @@ packages: - pkg:pypi/hyperframe?source=hash-mapping size: 17397 timestamp: 1737618427549 +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + sha256: 71e750d509f5fa3421087ba88ef9a7b9be11c53174af3aa4d06aff4c18b38e8e + md5: 8b189310083baabfb622af68fd9d3ae3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + purls: [] + size: 12129203 + timestamp: 1720853576813 - conda: https://conda.anaconda.org/conda-forge/win-64/icu-75.1-he0c23c2_0.conda sha256: 1d04369a1860a1e9e371b9fc82dd0092b616adcf057d6c88371856669280e920 md5: 8579b6bb8d18be7c0b27fb08adeeeb40 @@ -1565,20 +3208,33 @@ packages: purls: [] size: 14544252 timestamp: 1720853966338 -- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - sha256: d7a472c9fd479e2e8dcb83fb8d433fce971ea369d704ece380e876f9c3494e87 - md5: 39a4f67be3286c86d696df570b1201b7 +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + sha256: ae89d0299ada2a3162c2614a9d26557a92aa6a77120ce142f8e0109bbf0342b0 + md5: 53abe63df7e10a6ba605dc5f9f961d36 depends: - - python >=3.9 + - python >=3.10 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/idna?source=hash-mapping - size: 49765 - timestamp: 1733211921194 -- conda: https://conda.anaconda.org/conda-forge/win-64/imath-3.2.1-h1608b31_0.conda - sha256: df794e701df69dd63086b88a5a334353442a5e29f5999daa694d53bb59f4fd88 - md5: 6f07ed6b0206ebca3ff9c5ef4735bf23 + size: 50721 + timestamp: 1760286526795 +- conda: https://conda.anaconda.org/conda-forge/linux-64/imath-3.2.2-hde8ca8f_0.conda + sha256: 43f30e6fd8cbe1fef59da760d1847c9ceff3fb69ceee7fd4a34538b0927959dd + md5: c427448c6f3972c76e8a4474e0fe367b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 160289 + timestamp: 1759983212466 +- conda: https://conda.anaconda.org/conda-forge/win-64/imath-3.2.2-h1608b31_0.conda + sha256: 2aa57a5ed668ab34cc061b61082e3cee893d2e45ebc33f9432d13f7f292a990e + md5: 297d73cfad37a288459cf18286bb0e1f depends: - libzlib >=1.3.1,<2.0a0 - ucrt >=10.0.20348.0 @@ -1587,8 +3243,8 @@ packages: license: BSD-3-Clause license_family: BSD purls: [] - size: 162362 - timestamp: 1755292959085 + size: 162099 + timestamp: 1759983547586 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda sha256: c18ab120a0613ada4391b15981d86ff777b5690ca461ea7e9e49531e8f374745 md5: 63ccfdc3a3ce25b027b8767eb722fca8 @@ -1602,6 +3258,40 @@ packages: - pkg:pypi/importlib-metadata?source=hash-mapping size: 34641 timestamp: 1747934053147 +- conda: https://conda.anaconda.org/conda-forge/linux-64/intel-gmmlib-22.8.2-hb700be7_0.conda + sha256: 6bc45d77fb625cb9cd154cfb8c0783a3f21123dd9512b91439675c5f6163c29e + md5: 478edf896b4dfca175c27b052d76fbc2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 999849 + timestamp: 1757639263833 +- conda: https://conda.anaconda.org/conda-forge/linux-64/intel-media-driver-25.3.4-hecca717_0.conda + sha256: 286679d4c175e8db2d047be766d1629f1ea5828bff9fe7e6aac2e6f0fad2b427 + md5: 7ae2034a0e2e24eb07468f1a50cdf0bb + depends: + - __glibc >=2.17,<3.0.a0 + - intel-gmmlib >=22.8.1,<23.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libva >=2.22.0,<3.0a0 + license: MIT + license_family: MIT + purls: [] + size: 8424610 + timestamp: 1757591682198 +- conda: https://conda.anaconda.org/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda + sha256: 0fd2b0b84c854029041b0ede8f4c2369242ee92acc0092f8407b1fe9238a8209 + md5: 2d89243bfb53652c182a7c73182cce4f + license: LicenseRef-IntelSimplifiedSoftwareOct2022 + license_family: Proprietary + purls: [] + size: 1852356 + timestamp: 1723739573141 - conda: https://conda.anaconda.org/conda-forge/noarch/itsdangerous-2.2.0-pyhd8ed1ab_1.conda sha256: 1684b7b16eec08efef5302ce298c606b163c18272b69a62b666fbaa61516f170 md5: 7ac5f795c15f288984e32add616cdc59 @@ -1613,6 +3303,34 @@ packages: - pkg:pypi/itsdangerous?source=hash-mapping size: 19180 timestamp: 1733308353037 +- conda: https://conda.anaconda.org/conda-forge/linux-64/jack-1.9.22-hf4617a5_3.conda + sha256: 69f6b7228aa43177c82e21b90497adb1d7c61e70f1b8dd38d8ca8baa74a0cbf7 + md5: a071738556dc29fa1c844fb440506dc8 + depends: + - __glibc >=2.17,<3.0.a0 + - alsa-lib >=1.2.14,<1.3.0a0 + - libgcc >=13 + - libopus >=1.5.2,<2.0a0 + - libstdcxx >=13 + license: LGPL-2.0-only + license_family: LGPL + purls: [] + size: 461260 + timestamp: 1747574434594 +- conda: https://conda.anaconda.org/conda-forge/linux-64/jasper-4.2.8-he3c4edf_0.conda + sha256: 0e919ec86d980901d8cbb665e91f5e9bddb5ff662178f25aed6d63f999fd9afc + md5: a04073db11c2c86c555fb088acc8f8c1 + depends: + - __glibc >=2.17,<3.0.a0 + - freeglut >=3.2.2,<4.0a0 + - libgcc >=14 + - libglu >=9.0.3,<10.0a0 + - libglu >=9.0.3,<9.1.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + license: JasPer-2.0 + purls: [] + size: 681643 + timestamp: 1754514437930 - conda: https://conda.anaconda.org/conda-forge/win-64/jasper-4.2.8-h8ad263b_0.conda sha256: 67a171de9975e583d1cd860d67e67552b28bd992ed6d0b6b8f3311ff0f7fb6cf md5: f25a27d9c58ef3a63173f372edef0639 @@ -1645,6 +3363,7 @@ packages: - python >=3.10 - setuptools license: BSD-3-Clause + license_family: BSD purls: - pkg:pypi/joblib?source=hash-mapping size: 224671 @@ -1656,6 +3375,17 @@ packages: requires_dist: - six>=1.13.0 - editorconfig>=0.12.2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/jsoncpp-1.9.6-hf42df4d_1.conda + sha256: ed4b1878be103deb2e4c6d0eea3c9bdddfd7fc3178383927dce7578fb1063520 + md5: 7bdc5e2cc11cb0a0f795bdad9732b0f2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: LicenseRef-Public-Domain OR MIT + purls: [] + size: 169093 + timestamp: 1733780223643 - conda: https://conda.anaconda.org/conda-forge/win-64/jsoncpp-1.9.6-hda1637e_1.conda sha256: 5cbd1ca5b2196a9d2bce6bd3bab16674faedc2f7de56b726e8748128d81d0956 md5: 623fa3cfe037326999434d50c9362e90 @@ -1667,6 +3397,16 @@ packages: purls: [] size: 342126 timestamp: 1733780675474 +- conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda + sha256: 0960d06048a7185d3542d850986d807c6e37ca2e644342dd0c72feefcf26c2a4 + md5: b38117a3c920364aff79f870c984b4a3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + purls: [] + size: 134088 + timestamp: 1754905959823 - conda: https://conda.anaconda.org/conda-forge/win-64/khronos-opencl-icd-loader-2024.10.24-h2466b09_1.conda sha256: 881f92399f706df1185ec4372e59c5c9832f2dbb8e7587c6030a2a9a6e8ce7f8 md5: 71a72eb0eed16a4a76fd88359be48fec @@ -1680,9 +3420,24 @@ packages: purls: [] size: 46768 timestamp: 1732916943523 -- conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.4.9-py310h1e1005b_0.conda - sha256: 077f534795d9c012f028523ad3fe23a269e127e2b66f04963ee2f887ecb38796 - md5: 1dafe400279a912768c930ed12d65a29 +- conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py310haaf941d_2.conda + sha256: 5ef8337c7a89719427d25b0cdc776b34116fe988efc9bf56f5a2831d74b1584e + md5: 7426d76535fc6347f1b74f85fb17d6eb + depends: + - python + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.10.* *_cp310 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping + size: 78299 + timestamp: 1762488741951 +- conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.4.9-py310h1e1005b_2.conda + sha256: dbca5656a0e07dbc998d4d5e51497782d2e0d9c097a1072a9d4df5e2ef797dce + md5: 6b165d2b50fce619244bec7495bbbbc2 depends: - python - vc >=14.3,<15 @@ -1696,8 +3451,23 @@ packages: license_family: BSD purls: - pkg:pypi/kiwisolver?source=hash-mapping - size: 73318 - timestamp: 1754889412305 + size: 73319 + timestamp: 1762488749759 +- conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 + md5: 3f43953b7d3fb3aaa1d0d0723d91e368 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1370023 + timestamp: 1719463201255 - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.21.3-hdf4eb48_0.conda sha256: 18e8b3430d7d232dad132f574268f56b3eb1a19431d6d5de8c53c29e6c18fa81 md5: 31aec030344e962fbd7dbbbbd68e60a9 @@ -1711,6 +3481,16 @@ packages: purls: [] size: 712034 timestamp: 1719463874284 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2 + sha256: aad2a703b9d7b038c0f745b853c6bb5f122988fe1a7a096e0e606d9cbec4eaab + md5: a8832b479f93521a9e7b5b743803be51 + depends: + - libgcc-ng >=12 + license: LGPL-2.0-only + license_family: LGPL + purls: [] + size: 508258 + timestamp: 1664996250081 - conda: https://conda.anaconda.org/conda-forge/win-64/lame-3.100-hcfcfb64_1003.tar.bz2 sha256: 824988a396b97bb9138823a1b3aabd8326e06da5834b3011253d72bb45fd3a88 md5: d92e64077c44c9e32c72d4b5799d47e4 @@ -1723,6 +3503,19 @@ packages: purls: [] size: 570583 timestamp: 1664996824680 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda + sha256: d6a61830a354da022eae93fa896d0991385a875c6bba53c82263a289deda9db8 + md5: 000e85703f0fd9594c81710dd5066471 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + license: MIT + license_family: MIT + purls: [] + size: 248046 + timestamp: 1739160907615 - conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.17-hbcf6048_0.conda sha256: 7712eab5f1a35ca3ea6db48ead49e0d6ac7f96f8560da8023e61b3dbe4f3b25d md5: 3538827f77b82a837fa681a4579e37a1 @@ -1737,6 +3530,31 @@ packages: purls: [] size: 510641 timestamp: 1739161381270 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda + sha256: 9e191baf2426a19507f1d0a17be0fdb7aa155cdf0f61d5a09c808e0a69464312 + md5: a6abd2796fc332536735f68ba23f7901 + depends: + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - binutils_impl_linux-64 2.45 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 725545 + timestamp: 1764007826689 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda + sha256: 412381a43d5ff9bbed82cd52a0bbca5b90623f62e41007c9c42d3870c60945ff + md5: 9344155d33912347b37f0ae6c410a835 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 264243 + timestamp: 1745264221534 - conda: https://conda.anaconda.org/conda-forge/win-64/lerc-4.0.0-h6470a55_1.conda sha256: 868a3dff758cc676fa1286d3f36c3e0101cca56730f7be531ab84dc91ec58e9d md5: c1b81da6d29a14b542da14a36c9fbf3f @@ -1749,6 +3567,33 @@ packages: purls: [] size: 164701 timestamp: 1745264384716 +- conda: https://conda.anaconda.org/conda-forge/linux-64/level-zero-1.26.0-hb700be7_0.conda + sha256: 14db841b0ad250cb71ec83814c98a09f02f1402bc2bf75c9811d7a924996cbab + md5: 114cd93e761af141d7f5fa5570048425 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 638517 + timestamp: 1762297603883 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda + sha256: dcd1429a1782864c452057a6c5bc1860f2b637dc20a2b7e6eacd57395bbceff8 + md5: 83b160d4da3e1e847bf044997621ed63 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - libabseil-static =20250512.1=cxx17* + - abseil-cpp =20250512.1 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 1310612 + timestamp: 1750194198254 - conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20250512.1-cxx17_habfad5f_0.conda sha256: 78790771f44e146396d9ae92efbe1022168295afd8d174f653a1fa16f0f0fa32 md5: d6a4cd236fc1c69a1cfc9698fb5e391f @@ -1764,6 +3609,18 @@ packages: purls: [] size: 1615210 timestamp: 1750194549591 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda + sha256: 410ab78fe89bc869d435de04c9ffa189598ac15bb0fe1ea8ace8fb1b860a2aa3 + md5: 01ba04e414e47f95c03d6ddd81fd37be + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 36825 + timestamp: 1749993532943 - conda: https://conda.anaconda.org/conda-forge/win-64/libaec-1.1.4-h20038f6_0.conda sha256: 0be89085effce9fdcbb6aea7acdb157b18793162f68266ee0a75acf615d4929b md5: 85a2bed45827d77d5b308cb2b165404f @@ -1776,6 +3633,17 @@ packages: purls: [] size: 33847 timestamp: 1749993666162 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-0.25.1-h3f43e3d_1.conda + sha256: cb728a2a95557bb6a5184be2b8be83a6f2083000d0c7eff4ad5bbe5792133541 + md5: 3b0d184bc9404516d418d4509e418bdc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: LGPL-2.1-or-later + purls: [] + size: 53582 + timestamp: 1753342901341 - conda: https://conda.anaconda.org/conda-forge/win-64/libasprintf-0.22.5-h5728263_3.conda sha256: 8e41136b7e4ec44c1c0bae0ff51cdb0d04e026d0b44eaaf5a9ff8b4e1b6b019b md5: 9f661052be1d477dcf61ee3cd77ce5ee @@ -1783,6 +3651,50 @@ packages: purls: [] size: 49776 timestamp: 1723629333404 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-devel-0.25.1-h3f43e3d_1.conda + sha256: 2fc95060efc3d76547b7872875af0b7212d4b1407165be11c5f830aeeb57fc3a + md5: fd9cf4a11d07f0ef3e44fc061611b1ed + depends: + - __glibc >=2.17,<3.0.a0 + - libasprintf 0.25.1 h3f43e3d_1 + - libgcc >=14 + license: LGPL-2.1-or-later + purls: [] + size: 34734 + timestamp: 1753342921605 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libass-0.17.4-h96ad9f0_0.conda + sha256: 035eb8b54e03e72e42ef707420f9979c7427776ea99e0f1e3c969f92eb573f19 + md5: d3be7b2870bf7aff45b12ea53165babd + depends: + - libgcc >=13 + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.1,<2.0a0 + - libfreetype >=2.13.3 + - libfreetype6 >=2.13.3 + - fribidi >=1.0.10,<2.0a0 + - libiconv >=1.18,<2.0a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - harfbuzz >=11.0.1 + license: ISC + purls: [] + size: 152179 + timestamp: 1749328931930 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.3.0-h6395336_2.conda + sha256: e3a44c0eda23aa15c9a8dfa8c82ecf5c8b073e68a16c29edd0e409e687056d30 + md5: c09c4ac973f7992ba0c6bb1aafd77bd4 + depends: + - __glibc >=2.17,<3.0.a0 + - aom >=3.9.1,<3.10.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - libgcc >=14 + - rav1e >=0.7.1,<0.8.0a0 + - svt-av1 >=3.1.2,<3.1.3.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 139399 + timestamp: 1756124751131 - conda: https://conda.anaconda.org/conda-forge/win-64/libavif16-1.3.0-he916da2_2.conda sha256: dbb3f21282eccba6e4bd70c9db371e081bf09c55f1de7ca90f1106cc199d4a8b md5: 9782ce5bf5a3b41f29533c2c08f6b360 @@ -1800,25 +3712,62 @@ packages: purls: [] size: 116744 timestamp: 1756125168916 -- conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.9.0-34_h5709861_mkl.conda - build_number: 34 - sha256: d7865fcc7d29b22e4111ababec49083851a84bb3025748eed65184be765b6e7d - md5: a64dcde5f27b8e0e413ddfc56151664c +- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-2_h5875eb1_mkl.conda + build_number: 2 + sha256: f539ae2aa405175a6dc5781e7d40ed5ecea44340904c834bf4a789785b0e9eac + md5: 6a1a4ec47263069b2dae3cfba106320c + depends: + - mkl >=2025.3.0,<2026.0a0 + constrains: + - liblapack 3.11.0 2*_mkl + - liblapacke 3.11.0 2*_mkl + - blas 2.302 mkl + - libcblas 3.11.0 2*_mkl + track_features: + - blas_mkl + - blas_mkl_2 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18948 + timestamp: 1763828429422 +- conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.9.0-35_h5709861_mkl.conda + build_number: 35 + sha256: 4180e7ab27ed03ddf01d7e599002fcba1b32dcb68214ee25da823bac371ed362 + md5: 45d98af023f8b4a7640b1f713ce6b602 depends: - mkl >=2024.2.2,<2025.0a0 constrains: - - libcblas 3.9.0 34*_mkl - - liblapacke 3.9.0 34*_mkl - - blas 2.134 mkl - - liblapack 3.9.0 34*_mkl + - blas 2.135 mkl + - liblapack 3.9.0 35*_mkl + - libcblas 3.9.0 35*_mkl + - liblapacke 3.9.0 35*_mkl license: BSD-3-Clause license_family: BSD purls: [] - size: 70548 - timestamp: 1754682440057 -- conda: https://conda.anaconda.org/conda-forge/win-64/libboost-1.88.0-h9dfe17d_1.conda - sha256: 4199d989d9bdd66cc4cab63c3ae2ca8c7279ae889a2cc7be19a6bea7711b43f5 - md5: a19e51f7d5e5ee8d1ad0ef1376dc051b + size: 66044 + timestamp: 1757003486248 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-1.88.0-hed09d94_6.conda + sha256: 40b334d77229fcceb51d911a153d7ab9ff4f6a6f90e938387bf29129ab956c58 + md5: 70675d70a76e1b5539b1f464fd5f02ba + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - liblzma >=5.8.1,<6.0a0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - boost-cpp <0.0a0 + license: BSL-1.0 + purls: [] + size: 2978265 + timestamp: 1763017293494 +- conda: https://conda.anaconda.org/conda-forge/win-64/libboost-1.88.0-h9dfe17d_6.conda + sha256: 06866ea751e85b68a7ed1337a41fa11b65ad8948f79b2624839d1e4d1de21333 + md5: b749addb561373326d03a21f24be1059 depends: - bzip2 >=1.0.8,<2.0a0 - libiconv >=1.18,<2.0a0 @@ -1829,68 +3778,158 @@ packages: - vc14_runtime >=14.44.35208 - zstd >=1.5.7,<1.6.0a0 constrains: - - __win ==0|>=10 - boost-cpp <0.0a0 + - __win ==0|>=10 license: BSL-1.0 purls: [] - size: 2495267 - timestamp: 1755042179457 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.1.0-h2466b09_3.conda - sha256: e70ea4b773fadddda697306a80a29d9cbd36b7001547cd54cbfe9a97a518993f - md5: cf20c8b8b48ab5252ec64b9c66bfe0a4 + size: 2381816 + timestamp: 1763019598391 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + sha256: 318f36bd49ca8ad85e6478bd8506c88d82454cc008c1ac1c6bf00a3c42fa610e + md5: 72c8fd1af66bd67bf580645b426513ed + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 79965 + timestamp: 1764017188531 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hfd05255_1.conda + sha256: 5097303c2fc8ebf9f9ea9731520aa5ce4847d0be41764edd7f6dee2100b82986 + md5: 444b0a45bbd1cb24f82eedb56721b9c4 depends: - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + purls: [] + size: 82042 + timestamp: 1764017799966 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + sha256: 12fff21d38f98bc446d82baa890e01fd82e3b750378fedc720ff93522ffb752b + md5: 366b40a69f0ad6072561c1d09301c886 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 hb03c661_1 + - libgcc >=14 license: MIT license_family: MIT purls: [] - size: 71289 - timestamp: 1749230827419 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.1.0-h2466b09_3.conda - sha256: a35a0db7e3257e011b10ffb371735b2b24074412d0b27c3dab7ca9f2c549cfcf - md5: a342933dbc6d814541234c7c81cb5205 + size: 34632 + timestamp: 1764017199083 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-hfd05255_1.conda + sha256: 3239ce545cf1c32af6fffb7fc7c75cb1ef5b6ea8221c66c85416bb2d46f5cccb + md5: 450e3ae947fc46b60f1d8f8f318b40d4 depends: - - libbrotlicommon 1.1.0 h2466b09_3 + - libbrotlicommon 1.2.0 hfd05255_1 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + purls: [] + size: 34449 + timestamp: 1764017851337 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + sha256: a0c15c79997820bbd3fbc8ecf146f4fe0eca36cc60b62b63ac6cf78857f1dd0d + md5: 4ffbb341c8b616aa2494b6afb26a0c5f + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 hb03c661_1 + - libgcc >=14 license: MIT license_family: MIT purls: [] - size: 33451 - timestamp: 1749230869051 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.1.0-h2466b09_3.conda - sha256: 9d0703c5a01c10d346587ff0535a0eb81042364333caa4a24a0e4a0c08fd490b - md5: 7ef0af55d70cbd9de324bb88b7f9d81e + size: 298378 + timestamp: 1764017210931 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-hfd05255_1.conda + sha256: 3226df6b7df98734440739f75527d585d42ca2bfe912fbe8d1954c512f75341a + md5: ccd93cfa8e54fd9df4e83dbe55ff6e8c depends: - - libbrotlicommon 1.1.0 h2466b09_3 + - libbrotlicommon 1.2.0 hfd05255_1 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: MIT license_family: MIT purls: [] - size: 245845 - timestamp: 1749230909225 -- conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.9.0-34_h2a3cdd5_mkl.conda - build_number: 34 - sha256: e9f31d44e668822f6420bfaeda4aa74cd6c60d3671cf0b00262867f36ad5a8c1 - md5: 25a019872ff471af70fd76d9aaaf1313 + size: 252903 + timestamp: 1764017901735 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.77-h3ff7636_0.conda + sha256: 9517cce5193144af0fcbf19b7bd67db0a329c2cc2618f28ffecaa921a1cbe9d3 + md5: 09c264d40c67b82b49a3f3b89037bd2e + depends: + - __glibc >=2.17,<3.0.a0 + - attr >=2.5.2,<2.6.0a0 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 121429 + timestamp: 1762349484074 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-2_hfef963f_mkl.conda + build_number: 2 + sha256: 9c232b9527e8861fb708e30a0c51750ee3f33be393ab3f67b621287a53e456d6 + md5: 62ffd188ee5c953c2d6ac54662c158a7 + depends: + - libblas 3.11.0 2_h5875eb1_mkl + constrains: + - liblapack 3.11.0 2*_mkl + - liblapacke 3.11.0 2*_mkl + - blas 2.302 mkl + track_features: + - blas_mkl + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18557 + timestamp: 1763828437220 +- conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.9.0-35_h2a3cdd5_mkl.conda + build_number: 35 + sha256: 88939f6c1b5da75bd26ce663aa437e1224b26ee0dab5e60cecc77600975f397e + md5: 9639091d266e92438582d0cc4cfc8350 depends: - - libblas 3.9.0 34_h5709861_mkl + - libblas 3.9.0 35_h5709861_mkl constrains: - - liblapacke 3.9.0 34*_mkl - - blas 2.134 mkl - - liblapack 3.9.0 34*_mkl + - blas 2.135 mkl + - liblapack 3.9.0 35*_mkl + - liblapacke 3.9.0 35*_mkl license: BSD-3-Clause license_family: BSD purls: [] - size: 70700 - timestamp: 1754682490395 -- conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-20.1.8-default_hadf22e1_0.conda - sha256: b11a844f4d88f7785050b71ef1f70613100b518c02f23ec6401904a09820d8bf - md5: cf1a9a4c7395c5d6cc0dcf8f7c40acb3 + size: 66398 + timestamp: 1757003514529 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.6-default_h99862b1_0.conda + sha256: 314f4c4980c18138659fdd0c75385c1a88ff6bef2ac7890d1df76f9b2d5e1a5f + md5: 0fcc9b4d3fc5e5010a7098318d9b7971 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libllvm21 >=21.1.6,<21.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 21054536 + timestamp: 1763564022522 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.6-default_h746c552_0.conda + sha256: 83d89825255c0d0153687a74b69c460292d81876f5a71e94e22110702ad3e875 + md5: f5b64315835b284c7eb5332202b1e14b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libllvm21 >=21.1.6,<21.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 12339318 + timestamp: 1763564209593 +- conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-21.1.6-default_ha2db4b5_0.conda + sha256: d58668ad85b7c878d85f420893f51bc862825a94485c2d0b682dc6e6da3fbd01 + md5: 32b0f9f52f859396db50d738d50b4a82 depends: - libzlib >=1.3.1,<2.0a0 - ucrt >=10.0.20348.0 @@ -1900,61 +3939,186 @@ packages: license: Apache-2.0 WITH LLVM-exception license_family: Apache purls: [] - size: 28306754 - timestamp: 1752232456043 -- conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.14.1-h88aaa65_0.conda - sha256: b2cface2cf35d8522289df7fffc14370596db6f6dc481cc1b6ca313faeac19d8 - md5: 836b9c08f34d2017dbcaec907c6a1138 + size: 28997978 + timestamp: 1763567434596 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda + sha256: cb83980c57e311783ee831832eb2c20ecb41e7dee6e86e8b70b8cef0e43eab55 + md5: d4a250da4737ee127fb1fa6452a9002e + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 4523621 + timestamp: 1749905341688 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_0.conda + sha256: 100e29ca864c32af15a5cc354f502d07b2600218740fdf2439fa7d66b50b3529 + md5: 01e149d4a53185622dc2e788281961f2 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=14 + - libnghttp2 >=1.67.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + purls: [] + size: 460366 + timestamp: 1762333743748 +- conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.17.0-h43ecb02_0.conda + sha256: 651daa5d2bad505b5c72b1d5d4d8c7fc0776ab420e67af997ca9391b6854b1b3 + md5: cfade9be135edb796837e7d4c288c0fb depends: - krb5 >=1.21.3,<1.22.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.1,<2.0a0 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: curl license_family: MIT purls: [] - size: 368346 - timestamp: 1749033492826 -- conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.24-h76ddb4d_0.conda - sha256: 65347475c0009078887ede77efe60db679ea06f2b56f7853b9310787fe5ad035 - md5: 08d988e266c6ae77e03d164b83786dc4 + size: 378897 + timestamp: 1762333969177 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + sha256: aa8e8c4be9a2e81610ddf574e05b64ee131fab5e0e3693210c9d6d2fba32c680 + md5: 6c77a605a7a689d17d4819c0f8ac9a00 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 73490 + timestamp: 1761979956660 +- conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h51727cc_0.conda + sha256: 834e4881a18b690d5ec36f44852facd38e13afe599e369be62d29bd675f107ee + md5: e77030e67343e28b084fabd7db0ce43e depends: - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + purls: [] + size: 156818 + timestamp: 1761979842440 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda + sha256: c076a213bd3676cc1ef22eeff91588826273513ccc6040d9bea68bccdc849501 + md5: 9314bc5a1fe7d1044dc9dfd3ef400535 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpciaccess >=0.18,<0.19.0a0 + license: MIT + license_family: MIT + purls: [] + size: 310785 + timestamp: 1757212153962 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda + sha256: d789471216e7aba3c184cd054ed61ce3f6dac6f87a50ec69291b9297f8c18724 + md5: c277e0a4d549b03ac1e9d6cbbe3d017b + depends: + - ncurses + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - ncurses >=6.5,<7.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 134676 + timestamp: 1738479519902 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda + sha256: 7fd5408d359d05a969133e47af580183fbf38e2235b562193d427bb9dad79723 + md5: c151d5eb730e9b7480e6d48c0fc44048 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_2 + license: LicenseRef-libglvnd + purls: [] + size: 44840 + timestamp: 1731330973553 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 + md5: 172bf1cd1ff8629f2b1179945ed45055 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 112766 + timestamp: 1702146165126 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda + sha256: 1e1b08f6211629cbc2efe7a5bca5953f8f6b3cae0eeb04ca4dacee1bd4e2db2f + md5: 8b09ae86839581147ef2e5c5e229d164 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - expat 2.7.3.* license: MIT license_family: MIT purls: [] - size: 156292 - timestamp: 1747040812624 -- conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.1-hac47afa_0.conda - sha256: 8432ca842bdf8073ccecf016ccc9140c41c7114dc4ec77ca754551c01f780845 - md5: 3608ffde260281fa641e70d6e34b1b96 + size: 76643 + timestamp: 1763549731408 +- conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda + sha256: 844ab708594bdfbd7b35e1a67c379861bcd180d6efe57b654f482ae2f7f5c21e + md5: 8c9e4f1a0e688eef2e95711178061a0f depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - - expat 2.7.1.* + - expat 2.7.3.* + license: MIT + license_family: MIT + purls: [] + size: 70137 + timestamp: 1763550049107 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda + sha256: 25cbdfa65580cfab1b8d15ee90b4c9f1e0d72128f1661449c9a999d341377d54 + md5: 35f29eec58405aaf55e01cb470d8c26a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 license: MIT license_family: MIT purls: [] - size: 141322 - timestamp: 1752719767870 -- conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.4.6-h537db12_1.conda - sha256: d3b0b8812eab553d3464bbd68204f007f1ebadf96ce30eb0cbc5159f72e353f5 - md5: 85d8fa5e55ed8f93f874b3b23ed54ec6 + size: 57821 + timestamp: 1760295480630 +- conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda + sha256: ddff25aaa4f0aa535413f5d831b04073789522890a4d8626366e43ecde1534a3 + md5: ba4ad812d2afc22b9a34ce8327a0930f depends: - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: MIT license_family: MIT purls: [] - size: 44978 - timestamp: 1743435053850 + size: 44866 + timestamp: 1760295760649 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda + sha256: 65908b75fa7003167b8a8f0001e11e58ed5b1ef5e98b96ab2ba66d7c1b822c7d + md5: ee48bf17cc83a00f59ca1494d5646869 + depends: + - gettext >=0.21.1,<1.0a0 + - libgcc-ng >=12 + - libogg 1.3.* + - libogg >=1.3.4,<1.4.0a0 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 394383 + timestamp: 1687765514062 - conda: https://conda.anaconda.org/conda-forge/win-64/libflac-1.4.3-h63175ca_0.conda sha256: 965d1b9c957956a50797db24c031bdb3a604ef0e9a03713965513419aa1f99df md5: 9aca744e428be2ced2f35b421bf19afa @@ -1969,45 +4133,101 @@ packages: purls: [] size: 331119 timestamp: 1687766047396 -- conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.13.3-h57928b3_1.conda - sha256: e5bc7d0a8d11b7b234da4fcd9d78f297f7dec3fec8bd06108fd3ac7b2722e32e - md5: 410ba2c8e7bdb278dfbb5d40220e39d2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda + sha256: 4641d37faeb97cf8a121efafd6afd040904d4bca8c46798122f417c31d5dfbec + md5: f4084e4e6577797150f9b04a4560ceb0 depends: - - libfreetype6 >=2.13.3 + - libfreetype6 >=2.14.1 license: GPL-2.0-only OR FTL purls: [] - size: 8159 - timestamp: 1745370227235 -- conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.13.3-h0b5ce68_1.conda - sha256: 61308653e7758ff36f80a60d598054168a1389ddfbac46d7864c415fafe18e69 - md5: a84b7d1a13060a9372bea961a8131dbc + size: 7664 + timestamp: 1757945417134 +- conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.1-h57928b3_0.conda + sha256: 2029702ec55e968ce18ec38cc8cf29f4c8c4989a0d51797164dab4f794349a64 + md5: 3235024fe48d4087721797ebd6c9d28c depends: - - libpng >=1.6.47,<1.7.0a0 - - libzlib >=1.3.1,<2.0a0 + - libfreetype6 >=2.14.1 + license: GPL-2.0-only OR FTL + purls: [] + size: 8109 + timestamp: 1757946135015 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda + sha256: 4a7af818a3179fafb6c91111752954e29d3a2a950259c14a2fc7ba40a8b03652 + md5: 8e7251989bca326a28f4a5ffbd74557a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpng >=1.6.50,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - freetype >=2.14.1 + license: GPL-2.0-only OR FTL + purls: [] + size: 386739 + timestamp: 1757945416744 +- conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.1-hdbac1cb_0.conda + sha256: 223710600b1a5567163f7d66545817f2f144e4ef8f84e99e90f6b8a4e19cb7ad + md5: 6e7c5c5ab485057b5d07fd8188ba5c28 + depends: + - libpng >=1.6.50,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 constrains: - - freetype >=2.13.3 + - freetype >=2.14.1 license: GPL-2.0-only OR FTL purls: [] - size: 337007 - timestamp: 1745370226578 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-15.1.0-h1383e82_4.conda - sha256: c169606e148f8df3375fdc9fe76ee3f44b8ffc2515e8131ede8f2d75cf7d6f0c - md5: 59fe76f0ff39b512ff889459b9fc3054 + size: 340264 + timestamp: 1757946133889 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_13.conda + sha256: b405ccad7fc036e5570cb663dbee4f6db7fc3e8a023fe71918611380165657cd + md5: efdcbfd0069c808d48e6719f89c92e52 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + constrains: + - libgomp 15.2.0 he0feb66_13 + - libgcc-ng ==15.2.0=*_13 + license: GPL-3.0-only WITH GCC-exception-3.1 + purls: [] + size: 1040618 + timestamp: 1764187767359 +- conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-15.2.0-h8ee18e1_13.conda + sha256: fd05e7a82ea82643796ada8f799b0891e06f00645c4d669d0ea662b5f2632d91 + md5: a7c5d0f4e6797b47ac83135b7f675cab depends: - _openmp_mutex >=4.5 - libwinpthread >=12.0.0.r4.gg4f2fc60ca constrains: + - libgomp 15.2.0 h8ee18e1_13 + - libgcc-ng ==15.2.0=*_13 - msys2-conda-epoch <0.0a0 - - libgcc-ng ==15.1.0=*_4 - - libgomp 15.1.0 h1383e82_4 license: GPL-3.0-only WITH GCC-exception-3.1 + purls: [] + size: 814492 + timestamp: 1764191771887 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_13.conda + sha256: 8b4a7850c2976ea6e4924f99d042e6bc4b03b8a9cd46f17f6d3643a45ee8314d + md5: f1a67aa7f282dbb755dc8a86246989d3 + depends: + - libgcc 15.2.0 he0feb66_13 + license: GPL-3.0-only WITH GCC-exception-3.1 + purls: [] + size: 26983 + timestamp: 1764187778304 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.25.1-h3f43e3d_1.conda + sha256: 50a9e9815cf3f5bce1b8c5161c0899cc5b6c6052d6d73a4c27f749119e607100 + md5: 2f4de899028319b27eb7a4023be5dfd2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + license: GPL-3.0-or-later license_family: GPL purls: [] - size: 668220 - timestamp: 1753904114303 + size: 188293 + timestamp: 1753342911214 - conda: https://conda.anaconda.org/conda-forge/win-64/libgettextpo-0.22.5-h5728263_3.conda sha256: 6747bd29a0896b21ee1fe07bd212210475655354a3e8033c25b797e054ddd821 md5: e46c142e2d2d9ccef31ad3d176b10fab @@ -2019,62 +4239,235 @@ packages: purls: [] size: 171120 timestamp: 1723629671164 -- conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.84.3-h1c1036b_0.conda - sha256: bd322efaebc369e188a1dd93030325a40753a4c009e92c1f82ec481a20f0d232 - md5: 2bcc00752c158d4a70e1eaccbf6fe8ae +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.25.1-h3f43e3d_1.conda + sha256: c7ea10326fd450a2a21955987db09dde78c99956a91f6f05386756a7bfe7cc04 + md5: 3f7a43b3160ec0345c9535a9f0d7908e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgettextpo 0.25.1 h3f43e3d_1 + - libiconv >=1.18,<2.0a0 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 37407 + timestamp: 1753342931100 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_13.conda + sha256: bf0a0698318561a0027af6fc2aede086799414952c84d43be5c2ac720658f247 + md5: 0d1067fda8911da519d7fc15bced6e3e depends: - - libffi >=3.4.6,<3.5.0a0 + - libgfortran5 15.2.0 h68bc16d_13 + constrains: + - libgfortran-ng ==15.2.0=*_13 + license: GPL-3.0-only WITH GCC-exception-3.1 + purls: [] + size: 26951 + timestamp: 1764187818201 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_13.conda + sha256: 0f0b0f2b7a345eee5cdc7864a7794e196006b2b45d377a1ad2039afed054eb11 + md5: f5cd1c419503abab5196946dd9e7d99c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15.2.0 + constrains: + - libgfortran 15.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + purls: [] + size: 2482657 + timestamp: 1764187793388 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda + sha256: dc2752241fa3d9e40ce552c1942d0a4b5eeb93740c9723873f6fcf8d39ef8d2d + md5: 928b8be80851f5d8ffb016f9c81dae7a + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_2 + - libglx 1.7.0 ha4b6fd6_2 + license: LicenseRef-libglvnd + purls: [] + size: 134712 + timestamp: 1731330998354 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.2-h32235b2_0.conda + sha256: 918306d6ed211ab483e4e19368e5748b265d24e75c88a1c66a61f72b9fa30b29 + md5: 0cb0612bc9cb30c62baf41f9d600611b + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pcre2 >=10.46,<10.47.0a0 + constrains: + - glib 2.86.2 *_0 + license: LGPL-2.1-or-later + purls: [] + size: 3974801 + timestamp: 1763672326986 +- conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.86.2-hd9c3897_0.conda + sha256: 60fa317d11a6f5d4bc76be5ff89b9ac608171a00b206c688e3cc4f65c73b1bc4 + md5: fbd144e60009d93f129f0014a76512d3 + depends: + - libffi >=3.5.2,<3.6.0a0 - libiconv >=1.18,<2.0a0 - libintl >=0.22.5,<1.0a0 - libzlib >=1.3.1,<2.0a0 - - pcre2 >=10.45,<10.46.0a0 + - pcre2 >=10.46,<10.47.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - - glib 2.84.3 *_0 + - glib 2.86.2 *_0 license: LGPL-2.1-or-later purls: [] - size: 3826069 - timestamp: 1754315362939 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-15.1.0-h1383e82_4.conda - sha256: e4ce8693bc3250b98cbc41cc53116fb27ad63eaf851560758e8ccaf0e9b137aa - md5: 78582ad1a764f4a0dca2f3027a46cc5a + size: 3793396 + timestamp: 1763672587079 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglu-9.0.3-h5888daf_1.conda + sha256: a0105eb88f76073bbb30169312e797ed5449ebb4e964a756104d6e54633d17ef + md5: 8422fcc9e5e172c91e99aef703b3ce65 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libopengl >=1.7.0,<2.0a0 + - libstdcxx >=13 + license: SGI-B-2.0 + purls: [] + size: 325262 + timestamp: 1748692137626 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda + sha256: 1175f8a7a0c68b7f81962699751bb6574e6f07db4c9f72825f978e3016f46850 + md5: 434ca7e50e40f4918ab701e3facd59a0 + depends: + - __glibc >=2.17,<3.0.a0 + license: LicenseRef-libglvnd + purls: [] + size: 132463 + timestamp: 1731330968309 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda + sha256: 2d35a679624a93ce5b3e9dd301fff92343db609b79f0363e6d0ceb3a6478bfa7 + md5: c8013e438185f33b13814c5c488acd5c + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_2 + - xorg-libx11 >=1.8.10,<2.0a0 + license: LicenseRef-libglvnd + purls: [] + size: 75504 + timestamp: 1731330988898 +- conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-15.2.0-h8ee18e1_13.conda + sha256: 15aec6a14dd271f4dbfb2cd32e31da038f157e471a81d649650f7e10fa835111 + md5: d5d63fc45f84f3101f137f4e69a85a63 depends: - libwinpthread >=12.0.0.r4.gg4f2fc60ca constrains: - msys2-conda-epoch <0.0a0 license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL purls: [] - size: 535125 - timestamp: 1753904060607 -- conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h88281d1_1000.conda - sha256: 2fb437b82912c74b4869b66c601d52c77bb3ee8cb4812eab346d379f1c823225 - md5: e6298294e7612eccf57376a0683ddc80 + size: 651297 + timestamp: 1764191670369 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.74.1-h3288cfb_1.conda + sha256: 5a947f6ad314b430d54d17e8d13015dd98f902a928e2a3d36543d628bdd2208d + md5: 8142b6a4f2ffd81791262c8bd725b655 + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.34.5,<2.0a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libre2-11 >=2025.8.12 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.74.1 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 8902682 + timestamp: 1759629580676 +- conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.74.1-h317e13b_1.conda + sha256: 1f0db4a750f94c9a9186d7f534c61a70783558dff1618f46e365c327aa794c4e + md5: fb62412ebe0df97d36d8fef9e232ee5f + depends: + - c-ares >=1.34.5,<2.0a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libre2-11 >=2025.8.12 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - re2 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - grpc-cpp =1.74.1 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 15948811 + timestamp: 1759616170650 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.1-default_h7f8ec31_1002.conda + sha256: f7fbc792dbcd04bf27219c765c10c239937b34c6c1a1f77a5827724753e02da1 + md5: c01021ae525a76fe62720c7346212d74 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 2450642 + timestamp: 1757624375958 +- conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h64bd3f2_1002.conda + sha256: 266dfe151066c34695dbdc824ba1246b99f016115ef79339cbcf005ac50527c1 + md5: b0cac6e5b06ca5eeb14b4f7cf908619f depends: - libwinpthread >=12.0.0.r4.gg4f2fc60ca - - libxml2 >=2.13.8,<2.14.0a0 + - libxml2 + - libxml2-16 >=2.14.6 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD purls: [] - size: 2412139 - timestamp: 1752762145331 -- conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.3.0-h47aaa27_0.conda - sha256: 0c0d146bb142f86132356aabda2590498bd1dcae8396bbb7891954b6de9479e9 - md5: d175ef31c07023f9bc7f5deb274898c6 + size: 2414731 + timestamp: 1757624335056 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.3.0-h4c17acf_1.conda + sha256: 2bdd1cdd677b119abc5e83069bec2e28fe6bfb21ebaea3cd07acee67f38ea274 + md5: c2a0c1d0120520e979685034e0b79859 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: Apache-2.0 OR BSD-3-Clause + purls: [] + size: 1448617 + timestamp: 1758894401402 +- conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.3.0-ha71e874_1.conda + sha256: c722a04f065656b988a46dee87303ff0bf037179c50e2e76704b693def7f9a96 + md5: f4649d4b6bf40d616eda57d6255d2333 depends: - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 OR BSD-3-Clause purls: [] - size: 534523 - timestamp: 1755184733540 + size: 536186 + timestamp: 1758894243956 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + sha256: c467851a7312765447155e071752d7bf9bf44d610a5687e32706f480aad2833f + md5: 915f5995e94f60e9a4826e0b0920ee88 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-only + purls: [] + size: 790176 + timestamp: 1754908768807 - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda sha256: 0dcdb1a5f01863ac4e8ba006a8b0dc1a02d2221ec3319b5915a1863254d7efa7 md5: 64571d1dd6cdcfa25d0664a5950fdaa2 @@ -2095,25 +4488,52 @@ packages: purls: [] size: 95568 timestamp: 1723629479451 -- conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.0-h2466b09_0.conda - sha256: e61b0adef3028b51251124e43eb6edf724c67c0f6736f1628b02511480ac354e - md5: 7c51d27540389de84852daa1cdb9c63c +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda + sha256: cc9aba923eea0af8e30e0f94f2ad7156e2984d80d1e8e7fe6be5a1f257f0eb32 + md5: 8397539e3a0bbd1695584fb4f927485a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + purls: [] + size: 633710 + timestamp: 1762094827865 +- conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.2-hfd05255_0.conda + sha256: 795e2d4feb2f7fc4a2c6e921871575feb32b8082b5760726791f080d1e2c2597 + md5: 56a686f92ac0273c0f6af58858a3f013 depends: - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib purls: [] - size: 838154 - timestamp: 1745268437136 -- conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.11.1-h98f49f0_3.conda - sha256: a930401c905ac3e66a569242a3a0c5bd56aa54c6cece012440739bf134303420 - md5: 4763a7ba786df60509ef1b535fa49c29 + size: 841783 + timestamp: 1762094814336 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.1-hf08fa70_5.conda + sha256: 6b9524a6a7ea6ef1ac791b697f660c2898171ae505d12e6d27509b59cf059ee6 + md5: 82954a6f42e3fba59628741dca105c98 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libgcc >=14 + - libhwy >=1.3.0,<1.4.0a0 + - libstdcxx >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1740728 + timestamp: 1761788390905 +- conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.11.1-hac9b6f3_5.conda + sha256: 54e35ad6152fb705f26491c6651d4b77757315c446a494ffc477f36fb2203c79 + md5: 8e3cc52433c99ad9632f430d3ac2a077 depends: - - libbrotlidec >=1.1.0,<1.2.0a0 - - libbrotlienc >=1.1.0,<1.2.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 - libhwy >=1.3.0,<1.4.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 @@ -2121,38 +4541,99 @@ packages: license: BSD-3-Clause license_family: BSD purls: [] - size: 1093190 - timestamp: 1755256674833 -- conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.9.0-34_hf9ab0e9_mkl.conda - build_number: 34 - sha256: c65298d584551cba1b7a42537f8e0093ec9fd0e871fc80ddf9cf6ffa0efa25ae - md5: ba80d9feadfbafceafb0bf46d35f5886 + size: 1092699 + timestamp: 1761788697831 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-2_h5e43f62_mkl.conda + build_number: 2 + sha256: 18166b664d39650d3482420a7349082d4a8d79cee5ca69562160795ab6b0a808 + md5: 4f33d79eda3c82c95a54e8c2981adddb + depends: + - libblas 3.11.0 2_h5875eb1_mkl + constrains: + - liblapacke 3.11.0 2*_mkl + - blas 2.302 mkl + - libcblas 3.11.0 2*_mkl + track_features: + - blas_mkl + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18542 + timestamp: 1763828444709 +- conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.9.0-35_hf9ab0e9_mkl.conda + build_number: 35 + sha256: 56e0992fb58eed8f0d5fa165b8621fa150b84aa9af1467ea0a7a9bb7e2fced4f + md5: 0c6ed9d722cecda18f50f17fb3c30002 + depends: + - libblas 3.9.0 35_h5709861_mkl + constrains: + - blas 2.135 mkl + - libcblas 3.9.0 35*_mkl + - liblapacke 3.9.0 35*_mkl + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 78485 + timestamp: 1757003541803 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-2_hdba1596_mkl.conda + build_number: 2 + sha256: fb9d2c1ec2e92f6cd6cc303f322ba8a96b526702ac5713f3c640925ca1792534 + md5: 96dea51ff1435bd823020e25fd02da59 depends: - - libblas 3.9.0 34_h5709861_mkl + - libblas 3.11.0 2_h5875eb1_mkl + - libcblas 3.11.0 2_hfef963f_mkl + - liblapack 3.11.0 2_h5e43f62_mkl constrains: - - libcblas 3.9.0 34*_mkl - - liblapacke 3.9.0 34*_mkl - - blas 2.134 mkl + - blas 2.302 mkl + track_features: + - blas_mkl license: BSD-3-Clause license_family: BSD purls: [] - size: 82224 - timestamp: 1754682540087 -- conda: https://conda.anaconda.org/conda-forge/win-64/liblapacke-3.9.0-34_h3ae206f_mkl.conda - build_number: 34 - sha256: 24df1ddb68ed051cfede7b6d4a01ed774af644a3588d92e113968709b27fd594 - md5: d86c5cdb648873578fc733c0297f4c1c + size: 18595 + timestamp: 1763828452171 +- conda: https://conda.anaconda.org/conda-forge/win-64/liblapacke-3.9.0-35_h3ae206f_mkl.conda + build_number: 35 + sha256: c0de95203de56e518f35ca9ebefbcd3fd66c819fe7b178db584e97e1a494696d + md5: d777c47b311db74f7d4c76c9a0cb2306 depends: - - libblas 3.9.0 34_h5709861_mkl - - libcblas 3.9.0 34_h2a3cdd5_mkl - - liblapack 3.9.0 34_hf9ab0e9_mkl + - libblas 3.9.0 35_h5709861_mkl + - libcblas 3.9.0 35_h2a3cdd5_mkl + - liblapack 3.9.0 35_hf9ab0e9_mkl constrains: - - blas 2.134 mkl + - blas 2.135 mkl license: BSD-3-Clause license_family: BSD purls: [] - size: 87018 - timestamp: 1754682591174 + size: 82889 + timestamp: 1757003569605 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.6-hf7376ad_0.conda + sha256: 23010386efb545d68acbc4f9216c45f2b70a2f5398a6f389d70c9fee103648c4 + md5: 8aa154f30e0bc616cbde9794710e0be2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 44320002 + timestamp: 1763523422320 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzf-3.6-hb9d3cd8_3.conda + sha256: f4a4c1a0945dc97e22ddf495cbd38bc230b76a5fe6cec9f54ba0b00960329fb5 + md5: 5fff3766d959c2a6fd424ad325602383 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 18687 + timestamp: 1728095215498 - conda: https://conda.anaconda.org/conda-forge/win-64/liblzf-3.6-h2466b09_3.conda sha256: bf9554e91894f2f69cd56ed3dcfb02ad787479eceb258706fbd82fcc63ebb5f6 md5: 8ed6e27387fb8da1787f5e9841e75d07 @@ -2165,6 +4646,18 @@ packages: purls: [] size: 21089 timestamp: 1728095680706 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + sha256: f2591c0069447bbe28d4d696b7fcb0c5bd0b4ac582769b89addbcf26fb3430d8 + md5: 1a580f7796c7bf6393fddb8bbbde58dc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - xz 5.8.1.* + license: 0BSD + purls: [] + size: 112894 + timestamp: 1749230047870 - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda sha256: 55764956eb9179b98de7cc0e55696f2eff8f7b83fc3ebff5e696ca358bca28cc md5: c15148b2e18da456f5108ccb5e411446 @@ -2178,6 +4671,16 @@ packages: purls: [] size: 104935 timestamp: 1749230611612 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmad-0.15.1b-h0b41bf4_1001.conda + sha256: 9e94cec54c4baadaa652c761179b8d32771fe7fa55faf6c78c2e35f942367f74 + md5: dc5cc4700f02ffeecc48253c9f29025b + depends: + - libgcc-ng >=12 + license: GPL-2.0-only + license_family: GPL + purls: [] + size: 78561 + timestamp: 1670815547616 - conda: https://conda.anaconda.org/conda-forge/win-64/libmad-0.15.1b-hcfcfb64_1001.conda sha256: 466af4bb68679fd2c37b5811601cd80dabd7ba53b27e18a5a9ccab5138eb9aed md5: b6689654bd79a44e67f4d6ecca3d8b5b @@ -2190,9 +4693,35 @@ packages: purls: [] size: 92960 timestamp: 1670816129705 -- conda: https://conda.anaconda.org/conda-forge/win-64/libnetcdf-4.9.2-nompi_ha45073a_118.conda - sha256: f179694134c0d0ebc600f1ef0d6797c17a894fea8f089a91db6e7bc04e467b76 - md5: 54557b761dc20f53f504271208cd88c7 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.3-nompi_h11f7409_103.conda + sha256: e9a8668212719a91a6b0348db05188dfc59de5a21888db13ff8510918a67b258 + md5: 3ccff1066c05a1e6c221356eecc40581 + depends: + - __glibc >=2.17,<3.0.a0 + - attr >=2.5.2,<2.6.0a0 + - blosc >=1.21.6,<2.0a0 + - bzip2 >=1.0.8,<2.0a0 + - hdf4 >=4.2.15,<4.2.16.0a0 + - hdf5 >=1.14.6,<1.14.7.0a0 + - libaec >=1.1.4,<2.0a0 + - libcurl >=8.14.1,<9.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - libzip >=1.11.2,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.2,<4.0a0 + - zlib + - zstd >=1.5.7,<1.6.0a0 + license: MIT + license_family: MIT + purls: [] + size: 871447 + timestamp: 1757977084313 +- conda: https://conda.anaconda.org/conda-forge/win-64/libnetcdf-4.9.3-nompi_h7d90bef_103.conda + sha256: 675b55d2b9d5ad2d2fb8c1c2cc06b65c48b958d1faf7b8116a6bc352696ef8f0 + md5: 0c157867805749ddbf608766f1350e11 depends: - blosc >=1.21.6,<2.0a0 - bzip2 >=1.0.8,<2.0a0 @@ -2200,7 +4729,8 @@ packages: - hdf5 >=1.14.6,<1.14.7.0a0 - libaec >=1.1.4,<2.0a0 - libcurl >=8.14.1,<9.0a0 - - libxml2 >=2.13.8,<2.14.0a0 + - libxml2 + - libxml2-16 >=2.14.6 - libzip >=1.11.2,<2.0a0 - libzlib >=1.3.1,<2.0a0 - ucrt >=10.0.20348.0 @@ -2211,8 +4741,57 @@ packages: license: MIT license_family: MIT purls: [] - size: 626420 - timestamp: 1754055160171 + size: 678411 + timestamp: 1757977349918 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda + sha256: a4a7dab8db4dc81c736e9a9b42bdfd97b087816e029e221380511960ac46c690 + md5: b499ce4b026493a13774bcf0f4c33849 + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.34.5,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.2,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 666600 + timestamp: 1756834976695 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + sha256: 927fe72b054277cde6cb82597d0fcf6baf127dcbce2e0a9d8925a68f1265eef5 + md5: d864d34357c3b65a4b731f78c0801dc4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-only + license_family: GPL + purls: [] + size: 33731 + timestamp: 1750274110928 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda + sha256: 3b3f19ced060013c2dd99d9d46403be6d319d4601814c772a3472fe2955612b0 + md5: 7c7927b404672409d9917d49bff5f2d6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + purls: [] + size: 33418 + timestamp: 1734670021371 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.5-hd0c01bc_1.conda + sha256: ffb066ddf2e76953f92e06677021c73c85536098f1c21fcd15360dbc859e22e4 + md5: 68e52064ed3897463c0e958ab5c8f91b + depends: + - libgcc >=13 + - __glibc >=2.17,<3.0.a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 218500 + timestamp: 1745825989535 - conda: https://conda.anaconda.org/conda-forge/win-64/libogg-1.3.5-h2466b09_1.conda sha256: c63e5fb169dbd192aacdcee6e37235407f106b8ca9c9036942a25e0366cbc73c md5: b67ed8c9ca072695ff482e50d888a523 @@ -2228,23 +4807,79 @@ packages: purls: [] size: 35040 timestamp: 1745826086628 -- conda: https://conda.anaconda.org/conda-forge/win-64/libopencv-4.12.0-qt6_py310h5bb264c_603.conda - sha256: 0f050efe446b7db1103121c20eefa9c802b375dd4c4369c62aada853a1ad0311 - md5: 209f8d07575fdaad25921895e519e891 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopencv-4.12.0-qt6_py310hdb0ca46_607.conda + sha256: 782cf87d0288806e4c08cbc043eda11fc6d656404942ee200f5ac60572439da9 + md5: 0a3d3d37e6bda9efc287d2215b5c1179 depends: - - ffmpeg >=7.1.1,<8.0a0 - - harfbuzz >=11.4.3 + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - ffmpeg >=8.0.0,<9.0a0 + - harfbuzz >=12.1.0 + - hdf5 >=1.14.6,<1.14.7.0a0 + - imath >=3.2.2,<3.2.3.0a0 + - jasper >=4.2.8,<5.0a0 + - libasprintf >=0.25.1,<1.0a0 + - libavif16 >=1.3.0,<2.0a0 + - libcblas >=3.9.0,<4.0a0 + - libegl >=1.7.0,<2.0a0 + - libexpat >=2.7.1,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libgettextpo >=0.25.1,<1.0a0 + - libgl >=1.7.0,<2.0a0 + - libglib >=2.86.0,<3.0a0 + - libiconv >=1.18,<2.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - libjxl >=0.11,<0.12.0a0 + - liblapack >=3.9.0,<4.0a0 + - liblapacke >=3.9.0,<4.0a0 + - libopenvino >=2025.2.0,<2025.2.1.0a0 + - libopenvino-auto-batch-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-auto-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-hetero-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-intel-cpu-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-intel-gpu-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-intel-npu-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-ir-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-onnx-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-paddle-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-pytorch-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-tensorflow-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-tensorflow-lite-frontend >=2025.2.0,<2025.2.1.0a0 + - libpng >=1.6.50,<1.7.0a0 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - numpy >=1.21,<3 + - openexr >=3.4.1,<3.5.0a0 + - qt6-main >=6.9.3,<6.10.0a0 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/opencv-python?source=hash-mapping + - pkg:pypi/opencv-python-headless?source=hash-mapping + size: 32613795 + timestamp: 1760195158269 +- conda: https://conda.anaconda.org/conda-forge/win-64/libopencv-4.12.0-qt6_py310h3c58159_607.conda + sha256: 3be21d411a527b0e974e62fd720a706c8a03dc7e6bc003c844705e39e23a9d61 + md5: 22e274fc8b5f6dfb91336ba462d76ae9 + depends: + - ffmpeg >=8.0.0,<9.0a0 + - harfbuzz >=12.1.0 - hdf5 >=1.14.6,<1.14.7.0a0 - - imath >=3.2.1,<3.2.2.0a0 + - imath >=3.2.2,<3.2.3.0a0 - jasper >=4.2.8,<5.0a0 - libasprintf >=0.22.5,<1.0a0 - libavif16 >=1.3.0,<2.0a0 - libcblas >=3.9.0,<4.0a0 - libexpat >=2.7.1,<3.0a0 - - libfreetype >=2.13.3 - - libfreetype6 >=2.13.3 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 - libgettextpo >=0.22.5,<1.0a0 - - libglib >=2.84.3,<3.0a0 + - libglib >=2.86.0,<3.0a0 - libintl >=0.22.5,<1.0a0 - libjpeg-turbo >=3.1.0,<4.0a0 - libjxl >=0.11,<0.12.0a0 @@ -2264,12 +4899,12 @@ packages: - libopenvino-tensorflow-lite-frontend >=2025.2.0,<2025.2.1.0a0 - libpng >=1.6.50,<1.7.0a0 - libprotobuf >=6.31.1,<6.31.2.0a0 - - libtiff >=4.7.0,<4.8.0a0 + - libtiff >=4.7.1,<4.8.0a0 - libwebp-base >=1.6.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 - numpy >=1.21,<3 - - openexr >=3.3.5,<3.4.0a0 - - qt6-main >=6.9.1,<6.10.0a0 + - openexr >=3.4.1,<3.5.0a0 + - qt6-main >=6.9.3,<6.10.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -2278,8 +4913,30 @@ packages: purls: - pkg:pypi/opencv-python?source=hash-mapping - pkg:pypi/opencv-python-headless?source=hash-mapping - size: 34199859 - timestamp: 1755994707300 + size: 34037005 + timestamp: 1760196302618 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda + sha256: 215086c108d80349e96051ad14131b751d17af3ed2cb5a34edd62fa89bfe8ead + md5: 7df50d44d4a14d6c31a2c54f2cd92157 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_2 + license: LicenseRef-libglvnd + purls: [] + size: 50757 + timestamp: 1731330993524 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-2025.2.0-hb617929_1.conda + sha256: 235e7d474c90ad9d8955401b8a91dbe373aa1dc65db3c8232a5e22e4eaf41976 + md5: 1da20cc4ff32dc74424dec68ec087dba + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - pugixml >=1.15,<1.16.0a0 + - tbb >=2021.13.0 + purls: [] + size: 6244771 + timestamp: 1753211097492 - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-2025.2.0-hbf28c98_1.conda sha256: 0cc8b80948457a7e313a1b0ee53fe1f5f8bba1a1c2222c52c249b16a7b834cce md5: 3e8c920cd15b95793a6e95759ea23479 @@ -2292,6 +4949,18 @@ packages: purls: [] size: 3653369 timestamp: 1753212700978 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-auto-batch-plugin-2025.2.0-hed573e4_1.conda + sha256: 193f760e828b0dd5168dd1d28580d4bf429c5f14a4eee5e0c02ff4c6d4cf8093 + md5: 94f9d17be1d658213b66b22f63cc6578 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - tbb >=2021.13.0 + purls: [] + size: 114760 + timestamp: 1753211116381 - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-auto-batch-plugin-2025.2.0-hdd9a157_1.conda sha256: 9a5c0f8d328b4b30b25df41c3439afcee0fdc75387a761209ba0e9c1b466e68d md5: a01aadf9fb4770f0ae2703480fd201b8 @@ -2304,6 +4973,18 @@ packages: purls: [] size: 103160 timestamp: 1753212745480 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-auto-plugin-2025.2.0-hed573e4_1.conda + sha256: a6f9f996e64e6d2f295f017a833eda7018ff58b6894503272d72f0002dfd6f33 + md5: 071b3a82342715a411f216d379ab6205 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - tbb >=2021.13.0 + purls: [] + size: 250500 + timestamp: 1753211127339 - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-auto-plugin-2025.2.0-hdd9a157_1.conda sha256: 1ca478a8cf77109a991d9d2b1f3e450e6f859e88dbbdad14487c52219b3ccd68 md5: 3def5acd94f3b9f17fc5271ac588f65f @@ -2316,6 +4997,18 @@ packages: purls: [] size: 200599 timestamp: 1753212785298 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-hetero-plugin-2025.2.0-hd41364c_1.conda + sha256: f43f9049338ef9735b6815bac3f483d1e3adddecbfdeb13be365bc3f601fe156 + md5: 77c0c7028a8110076d40314dc7b1fa98 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - pugixml >=1.15,<1.16.0a0 + purls: [] + size: 194815 + timestamp: 1753211138624 - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-hetero-plugin-2025.2.0-hc39e7c6_1.conda sha256: c3b342da0bec3acb02cec6b0d62fe07da9453b137e6e28d67a8177ceb6baf8a2 md5: 684e08a73b21ae6de11e42237b96e618 @@ -2328,6 +5021,19 @@ packages: purls: [] size: 162722 timestamp: 1753212823850 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-cpu-plugin-2025.2.0-hb617929_1.conda + sha256: a4a1cd320fa010a45d01f438dc3431b7a60271ee19188a901f884399fe744268 + md5: e4cc6db5bdc8b554c06bf569de57f85f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - pugixml >=1.15,<1.16.0a0 + - tbb >=2021.13.0 + purls: [] + size: 12377488 + timestamp: 1753211149903 - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-intel-cpu-plugin-2025.2.0-hbf28c98_1.conda sha256: 291db415e646b17868455c38d6c7594c8ca7f14734865b7f132ca5b56f9642d4 md5: 4d3f5ec9fdbe5812bf60c3e41ffa5182 @@ -2341,6 +5047,20 @@ packages: purls: [] size: 7706980 timestamp: 1753212869680 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-gpu-plugin-2025.2.0-hb617929_1.conda + sha256: 03ebf700586775144ca5913f401393a386b9a1d7a7cfcba4494830063ca5eb92 + md5: b846fe6c158ca417e246122172d68d3a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - ocl-icd >=2.3.3,<3.0a0 + - pugixml >=1.15,<1.16.0a0 + - tbb >=2021.13.0 + purls: [] + size: 10815480 + timestamp: 1753211182626 - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-intel-gpu-plugin-2025.2.0-hbf28c98_1.conda sha256: e8145dc7d29c952976d8cf3cefc9793adecef15d39fa650fee9fafe4168bbe68 md5: 3e9f4e7e62dfb2f51b15e5511be05eb4 @@ -2355,6 +5075,32 @@ packages: purls: [] size: 8055624 timestamp: 1753212931557 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-npu-plugin-2025.2.0-hb617929_1.conda + sha256: b6dbc342293d6ce0c7b37c9f29f734b3e1856cff9405a02fb33cedd1b36528e6 + md5: 86fd4c25f6accaf646c86adf0f1382d3 + depends: + - __glibc >=2.17,<3.0.a0 + - level-zero >=1.23.1,<2.0a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - pugixml >=1.15,<1.16.0a0 + - tbb >=2021.13.0 + purls: [] + size: 1261488 + timestamp: 1753211212823 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-ir-frontend-2025.2.0-hd41364c_1.conda + sha256: 334733396d4c9a9b2b2d7d7d850e8ee8deca1f9becd0368d106010076ceb20ca + md5: 75e595d9f2019a60f6dcb500266da615 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - pugixml >=1.15,<1.16.0a0 + purls: [] + size: 204890 + timestamp: 1753211224567 - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-ir-frontend-2025.2.0-hc39e7c6_1.conda sha256: 23e27eedc47b32fa6198725166cf4624c8ad2041cc21a6482d8b08e484d7c158 md5: e6d40d52179edb9debb43fd248c4aedf @@ -2367,6 +5113,20 @@ packages: purls: [] size: 162735 timestamp: 1753212992971 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-onnx-frontend-2025.2.0-h1862bb8_1.conda + sha256: 3937b028e7192ed3805581ac0ea171725843056c8544537754fad45a1791e864 + md5: 68f5ad9d8e3979362bb9dfc9388980aa + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + purls: [] + size: 1724503 + timestamp: 1753211235981 - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-onnx-frontend-2025.2.0-hee3bb10_1.conda sha256: ddc936da69789f7b8efd8db64a5bc9e363609476d73e8c640f2eb2cf6cdb6f04 md5: cea632435cc1be3cefdf3720ba858afb @@ -2381,6 +5141,20 @@ packages: purls: [] size: 1054345 timestamp: 1753213034424 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-paddle-frontend-2025.2.0-h1862bb8_1.conda + sha256: c7ac3d4187323ab37ef62ec0896a41c8ca7da426c7f587494c72fe74852269e5 + md5: a032d03468dee9fb5b8eaf635b4571c2 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + purls: [] + size: 744746 + timestamp: 1753211248776 - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-paddle-frontend-2025.2.0-hee3bb10_1.conda sha256: 50ff4bc294627a55cc262889eeb44966754af152c6ba2ae1b4b623668b4065c4 md5: e6a2327d63d087afdccbdb91b4f96011 @@ -2395,6 +5169,17 @@ packages: purls: [] size: 426107 timestamp: 1753213077249 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-pytorch-frontend-2025.2.0-hecca717_1.conda + sha256: 2d4a680a16509b8dd06ccd7a236655e46cc7c242bb5b6e88b83a834b891658db + md5: cd40cf2d10a3279654c9769f3bc8caf5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + purls: [] + size: 1243134 + timestamp: 1753211260154 - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-pytorch-frontend-2025.2.0-hac47afa_1.conda sha256: 086c8900f542733ad081710546c973e76622132490d1079f4ac255ef831d3d64 md5: 3b565a100920e7d8490e20d7469b8a7d @@ -2406,6 +5191,21 @@ packages: purls: [] size: 704269 timestamp: 1753213117834 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-frontend-2025.2.0-h0767aad_1.conda + sha256: 311ec1118448a28e76f0359c4393c7f7f5e64761c48ac7b169bf928a391eae77 + md5: f71c6b4e342b560cc40687063ef62c50 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + - snappy >=1.2.2,<1.3.0a0 + purls: [] + size: 1325059 + timestamp: 1753211272484 - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-tensorflow-frontend-2025.2.0-h293fe96_1.conda sha256: a88759c403407a444c5f26ba8f6a0e8147fbb729ebc6cdd6fb960aa74946c7a8 md5: a2316124f52e573a9310ebc0e679ea00 @@ -2421,6 +5221,17 @@ packages: purls: [] size: 869632 timestamp: 1753213165019 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-lite-frontend-2025.2.0-hecca717_1.conda + sha256: 581f4951e645e820c4a6ffe40fb0174b56d6e31fb1fefd2d64913fea01f8f69e + md5: fd9dacd7101f80ff1110ea6b76adb95d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + purls: [] + size: 497047 + timestamp: 1753211285617 - conda: https://conda.anaconda.org/conda-forge/win-64/libopenvino-tensorflow-lite-frontend-2025.2.0-hac47afa_1.conda sha256: e8f067eca90af64bd9d23453e83ff50782fbfaffcc682a6beb02794d8b34ced8 md5: 8d399fe1f39b8baa11ad925dd1c14723 @@ -2432,6 +5243,17 @@ packages: purls: [] size: 334957 timestamp: 1753213206109 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopus-1.5.2-hd0c01bc_0.conda + sha256: 786d43678d6d1dc5f88a6bad2d02830cfd5a0184e84a8caa45694049f0e3ea5f + md5: b64523fb87ac6f87f0790f324ad43046 + depends: + - libgcc >=13 + - __glibc >=2.17,<3.0.a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 312472 + timestamp: 1744330953241 - conda: https://conda.anaconda.org/conda-forge/win-64/libopus-1.5.2-h2466b09_0.conda sha256: 4c5e04de758450f9427a75095a54957de521b57234711374fac1cdc89fc7a9ca md5: 67c18f2110921f6307a608050cd153f8 @@ -2447,9 +5269,31 @@ packages: purls: [] size: 289268 timestamp: 1744330990400 -- conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.50-h7351971_1.conda - sha256: e84b041f91c94841cb9b97952ab7f058d001d4a15ed4ce226ec5fdb267cc0fa5 - md5: 3ae6e9f5c47c495ebeed95651518be61 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda + sha256: 0bd91de9b447a2991e666f284ae8c722ffb1d84acb594dbd0c031bd656fa32b2 + md5: 70e3400cbbfa03e96dcde7fc13e38c7b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 28424 + timestamp: 1749901812541 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.51-h421ea60_0.conda + sha256: 1eb769c0f2778d07428947f64272592cc2d3b9bce63b41600abe5dc2b683d829 + md5: d8b81203d08435eb999baa249427884e + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + purls: [] + size: 317576 + timestamp: 1763764145606 +- conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.51-h7351971_0.conda + sha256: 4a558e1901cc67b1c336cf719dfa1b806c5e69492df9fe6c19991da57a6845d2 + md5: 5b98079b7e86c25c7e70ed7fd7da7da5 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -2460,11 +5304,40 @@ packages: - libzlib >=1.3.1,<2.0a0 license: zlib-acknowledgement purls: [] - size: 382709 - timestamp: 1753879944850 -- conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-6.31.1-hdcda5b4_1.conda - sha256: 085b55d51328c8fcd6aef15f717a21d921bf8df1db2adfa81036e041a0609cd4 - md5: f046835750b70819a1e2fffddf111825 + size: 383255 + timestamp: 1763764166376 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.1-h5c52fec_1.conda + sha256: 22a2e4a5054143641545e87ec42aa7da745c6823587aa319dc86c3486698fa2c + md5: 638350cf5da41f3651958876a2104992 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=14 + - openldap >=2.6.10,<2.7.0a0 + - openssl >=3.5.4,<4.0a0 + license: PostgreSQL + purls: [] + size: 2907791 + timestamp: 1763518791620 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_2.conda + sha256: 1679f16c593d769f3dab219adb1117cbaaddb019080c5a59f79393dc9f45b84f + md5: 94cb88daa0892171457d9fdc69f43eca + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 4645876 + timestamp: 1760550892361 +- conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-6.31.1-hdcda5b4_2.conda + sha256: bb28909aef3777c5e950b769b30fe4bf02e0a7fb5322e583042a5cdc76bb15d0 + md5: 0e44c704760bbe4b696d981c3313f665 depends: - libabseil * cxx17* - libabseil >=20250512.1,<20250513.0a0 @@ -2475,24 +5348,99 @@ packages: license: BSD-3-Clause license_family: BSD purls: [] - size: 7615542 - timestamp: 1751690551169 -- conda: https://conda.anaconda.org/conda-forge/win-64/librsvg-2.58.4-h5ce5fed_3.conda - sha256: 8910bc40a52f2b979ced95137f09b8faf0113e14c430ca8fa7dd94dc88dafb83 - md5: 34fefcb3aed33ea39f1b040f5b9849e3 + size: 7787239 + timestamp: 1760550955606 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h7b12aa8_0.conda + sha256: eb5d5ef4d12cdf744e0f728b35bca910843c8cf1249f758cf15488ca04a21dbb + md5: a30848ebf39327ea078cf26d114cff53 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - re2 2025.11.05.* + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 211099 + timestamp: 1762397758105 +- conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2025.11.05-h0eb2380_0.conda + sha256: 8eb2c205588e6d751fe387e90f1321ac8bbaef0a12d125a1dd898e925327f8ae + md5: 960713477ad3d7f82e5199fa1b940495 depends: + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - re2 2025.11.05.* + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 263996 + timestamp: 1762397947932 +- conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.60.0-h61e6d4b_0.conda + sha256: 960b137673b2b8293e2a12d194add72967b3bf12fcdf691e7ad8bd5c8318cec3 + md5: 91e6d4d684e237fba31b9815c4b40edf + depends: + - __glibc >=2.17,<3.0.a0 - cairo >=1.18.4,<2.0a0 - - gdk-pixbuf >=2.42.12,<3.0a0 - - libglib >=2.84.0,<3.0a0 - - libxml2 >=2.13.7,<2.14.0a0 - - pango >=1.56.3,<2.0a0 + - gdk-pixbuf >=2.44.3,<3.0a0 + - libgcc >=14 + - libglib >=2.86.0,<3.0a0 + - libxml2-16 >=2.14.6 + - pango >=1.56.4,<2.0a0 + constrains: + - __glibc >=2.17 + license: LGPL-2.1-or-later + purls: [] + size: 3421977 + timestamp: 1759327942156 +- conda: https://conda.anaconda.org/conda-forge/win-64/librsvg-2.60.0-hd5e4115_0.conda + sha256: a0e8d89c36e555149f3ba2d58bb96f1b77e8ed7924db8a242ee0b0fb613c588d + md5: 5b38f886aa0548d6f6f5d1a30b3ae0ca + depends: + - cairo >=1.18.4,<2.0a0 + - gdk-pixbuf >=2.44.3,<3.0a0 + - libglib >=2.86.0,<3.0a0 + - libxml2-16 >=2.14.6 + - pango >=1.56.4,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - - vc14_runtime >=14.42.34438 + - vc14_runtime >=14.44.35208 license: LGPL-2.1-or-later purls: [] - size: 3919170 - timestamp: 1743369262131 + size: 3336793 + timestamp: 1759328441569 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda + sha256: f709cbede3d4f3aee4e2f8d60bd9e256057f410bd60b8964cb8cf82ec1457573 + md5: ef1910918dd895516a769ed36b5b3a4e + depends: + - lame >=3.100,<3.101.0a0 + - libflac >=1.4.3,<1.5.0a0 + - libgcc-ng >=12 + - libogg >=1.3.4,<1.4.0a0 + - libopus >=1.3.1,<2.0a0 + - libstdcxx-ng >=12 + - libvorbis >=1.3.7,<1.4.0a0 + - mpg123 >=1.32.1,<1.33.0a0 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + size: 354372 + timestamp: 1695747735668 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + sha256: 0105bd108f19ea8e6a78d2d994a6d4a8db16d19a41212070d2d1d48a63c34161 + md5: a587892d3c13b6621a6091be690dbca2 + depends: + - libgcc-ng >=12 + license: ISC + purls: [] + size: 205978 + timestamp: 1716828628198 - conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.20-hc70643c_0.conda sha256: 7bcb3edccea30f711b6be9601e083ecf4f435b9407d70fc48fbcf9e5d69a0fc6 md5: 198bb594f202b205c7d18b936fa4524f @@ -2504,17 +5452,42 @@ packages: purls: [] size: 202344 timestamp: 1716828757533 -- conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.50.4-hf5d6505_0.conda - sha256: 5dc4f07b2d6270ac0c874caec53c6984caaaa84bc0d3eb593b0edf3dc8492efa - md5: ccb20d946040f86f0c05b644d5eadeca +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.0-hee844dc_0.conda + sha256: 4c992dcd0e34b68f843e75406f7f303b1b97c248d18f3c7c330bdc0bc26ae0b3 + md5: 729a572a3ebb8c43933b30edcc628ceb + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + license: blessing + purls: [] + size: 945576 + timestamp: 1762299687230 +- conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.0-hf5d6505_0.conda + sha256: 2373bd7450693bd0f624966e1bee2f49b0bf0ffbc114275ed0a43cf35aec5b21 + md5: d2c9300ebd2848862929b18c264d1b1e depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: blessing purls: [] - size: 1288499 - timestamp: 1753948889360 + size: 1292710 + timestamp: 1762299749044 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda + sha256: fa39bfd69228a13e553bd24601332b7cfeb30ca11a3ca50bb028108fe90a7661 + md5: eecce068c7e4eddeb169591baac20ac4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 304790 + timestamp: 1745608545575 - conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda sha256: cbdf93898f2e27cefca5f3fe46519335d1fab25c4ea2a11b11502ff63e602c09 md5: 9dce2f112bfd3400f4f432b3d0ac07b2 @@ -2529,6 +5502,52 @@ packages: purls: [] size: 292785 timestamp: 1745608759342 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_13.conda + sha256: f7c363c5ec37213d2eb490dd76d2acdfb765e41e9f4d9f0d785f3aaada28e039 + md5: b98186396f799a0c15d5171f39f8b151 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc 15.2.0 he0feb66_13 + constrains: + - libstdcxx-ng ==15.2.0=*_13 + license: GPL-3.0-only WITH GCC-exception-3.1 + purls: [] + size: 5857033 + timestamp: 1764187811790 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_13.conda + sha256: 60e0b56a9e8e43efa40763765463d0ef7f634b1971be620a5dbce308a2de35a2 + md5: 64638ce314654548491c21abde2ff8ad + depends: + - libstdcxx 15.2.0 h934c35e_13 + license: GPL-3.0-only WITH GCC-exception-3.1 + purls: [] + size: 27032 + timestamp: 1764187856835 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.10-hd0affe5_2.conda + sha256: b30c06f60f03c2cf101afeb3452f48f12a2553b4cb631c9460c8a8ccf0813ae5 + md5: b04e0a2163a72588a40cde1afd6f2d18 + depends: + - __glibc >=2.17,<3.0.a0 + - libcap >=2.77,<2.78.0a0 + - libgcc >=14 + license: LGPL-2.1-or-later + purls: [] + size: 491211 + timestamp: 1763011323224 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtheora-1.1.1-h4ab18f5_1006.conda + sha256: 50c8cd416ac8425e415264de167b41ae8442de22a91098dfdd993ddbf9f13067 + md5: 553281a034e9cf8693c9df49f6c78ea1 + depends: + - libgcc-ng >=12 + - libogg 1.3.* + - libogg >=1.3.5,<1.4.0a0 + - libvorbis 1.3.* + - libvorbis >=1.3.7,<1.4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 328924 + timestamp: 1719667859099 - conda: https://conda.anaconda.org/conda-forge/win-64/libtheora-1.1.1-hc70643c_1006.conda sha256: 7c4f8dca38604fa17d54061ff03f3e79aff78537a12e1eaf3b4a01be743b5633 md5: 90cdca71edde0b3e549e8cbb43308208 @@ -2543,12 +5562,30 @@ packages: purls: [] size: 160440 timestamp: 1719668116346 -- conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.0-h550210a_6.conda - sha256: fd27821c8cfc425826f13760c3263d7b3b997c5372234cefa1586ff384dcc989 - md5: 72d45aa52ebca91aedb0cfd9eac62655 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda + sha256: e5f8c38625aa6d567809733ae04bb71c161a42e44a9fa8227abe61fa5c60ebe0 + md5: cd5a90476766d53e901500df9215e927 + depends: + - __glibc >=2.17,<3.0.a0 + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.25,<1.26.0a0 + - libgcc >=14 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblzma >=5.8.1,<6.0a0 + - libstdcxx >=14 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + purls: [] + size: 435273 + timestamp: 1762022005702 +- conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.1-h8f73337_1.conda + sha256: f1b8cccaaeea38a28b9cd496694b2e3d372bb5be0e9377c9e3d14b330d1cba8a + md5: 549845d5133100142452812feb9ba2e8 depends: - lerc >=4.0.0,<5.0a0 - - libdeflate >=1.24,<1.25.0a0 + - libdeflate >=1.25,<1.26.0a0 - libjpeg-turbo >=3.1.0,<4.0a0 - liblzma >=5.8.1,<6.0a0 - libzlib >=1.3.1,<2.0a0 @@ -2558,8 +5595,54 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: HPND purls: [] - size: 983988 - timestamp: 1755012056987 + size: 993166 + timestamp: 1762022118895 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libudev1-257.10-hd0affe5_2.conda + sha256: 751cf346f0f56cc9bfa43f7b5c9c30df2fcec8d84d164ac0cd74a27a3af79f30 + md5: 2f6b30acaa0d6e231d01166549108e2c + depends: + - __glibc >=2.17,<3.0.a0 + - libcap >=2.77,<2.78.0a0 + - libgcc >=14 + license: LGPL-2.1-or-later + purls: [] + size: 144395 + timestamp: 1763011330153 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda + sha256: 71c8b9d5c72473752a0bb6e91b01dd209a03916cb71f36cc6a564e3a2a132d7a + md5: e179a69edd30d75c0144d7a380b88f28 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 75995 + timestamp: 1757032240102 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liburing-2.12-hb700be7_0.conda + sha256: 880b1f76b24814c9f07b33402e82fa66d5ae14738a35a943c21c4434eef2403d + md5: f0531fc1ebc0902555670e9cb0127758 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 127967 + timestamp: 1756125594973 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libusb-1.0.29-h73b1eb8_0.conda + sha256: 89c84f5b26028a9d0f5c4014330703e7dff73ba0c98f90103e9cef6b43a5323c + md5: d17e3fb595a9f24fa9e149239a33475d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libudev1 >=257.4 + license: LGPL-2.1-or-later + purls: [] + size: 89551 + timestamp: 1748856210075 - conda: https://conda.anaconda.org/conda-forge/win-64/libusb-1.0.29-h1839187_0.conda sha256: 9837f8e8de20b6c9c033561cd33b4554cd551b217e3b8d2862b353ed2c23d8b8 md5: a656b2c367405cd24988cf67ff2675aa @@ -2574,6 +5657,53 @@ packages: purls: [] size: 118204 timestamp: 1748856290542 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda + sha256: e5ec6d2ad7eef538ddcb9ea62ad4346fde70a4736342c4ad87bd713641eb9808 + md5: 80c07c68d2f6870250959dcc95b209d1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 37135 + timestamp: 1758626800002 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libva-2.22.0-h4f16b4b_2.conda + sha256: e0df324fb02fa05a05824b8db886b06659432b5cff39495c59e14a37aa23d40f + md5: 2c65566e79dc11318ce689c656fb551c + depends: + - __glibc >=2.17,<3.0.a0 + - libdrm >=2.4.124,<2.5.0a0 + - libegl >=1.7.0,<2.0a0 + - libgcc >=13 + - libgl >=1.7.0,<2.0a0 + - libglx >=1.7.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - wayland >=1.23.1,<2.0a0 + - wayland-protocols + - xorg-libx11 >=1.8.11,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 217567 + timestamp: 1740897682004 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h54a6638_2.conda + sha256: ca494c99c7e5ecc1b4cd2f72b5584cef3d4ce631d23511184411abcbb90a21a5 + md5: b4ecbefe517ed0157c37f8182768271c + depends: + - libogg + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - libogg >=1.3.5,<1.4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 285894 + timestamp: 1753879378005 - conda: https://conda.anaconda.org/conda-forge/win-64/libvorbis-1.3.7-h5112557_2.conda sha256: 429124709c73b2e8fae5570bdc6b42f5418a7551ba72e591bb960b752e87b365 md5: 42a8a56c60882da5d451aa95b8455111 @@ -2591,6 +5721,78 @@ packages: purls: [] size: 243401 timestamp: 1753879416570 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libvpl-2.15.0-h54a6638_1.conda + sha256: bf0010d93f5b154c59bd9d3cc32168698c1d24f2904729f4693917cce5b27a9f + md5: a41a299c157cc6d0eff05e5fc298cc45 + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - intel-media-driver >=25.3.3,<25.4.0a0 + - libva >=2.22.0,<3.0a0 + license: MIT + license_family: MIT + purls: [] + size: 287944 + timestamp: 1757278954789 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libvpx-1.14.1-hac33072_0.conda + sha256: e7d2daf409c807be48310fcc8924e481b62988143f582eb3a58c5523a6763b13 + md5: cde393f461e0c169d9ffb2fc70f81c33 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1022466 + timestamp: 1717859935011 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda + sha256: bbabc5c48b63ff03f440940a11d4648296f5af81bb7630d98485405cd32ac1ce + md5: 372a62464d47d9e966b630ffae3abe73 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxrandr >=1.5.4,<2.0a0 + constrains: + - libvulkan-headers 1.4.328.1.* + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 197672 + timestamp: 1759972155030 +- conda: https://conda.anaconda.org/conda-forge/win-64/libvulkan-loader-1.4.328.1-h477610d_0.conda + sha256: 934d676c445c1ea010753dfa98680b36a72f28bec87d15652f013c91a1d8d171 + md5: 4403eae6c81f448d63a7f66c0b330536 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + constrains: + - libvulkan-headers 1.4.328.1.* + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 280488 + timestamp: 1759972163692 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda + sha256: 3aed21ab28eddffdaf7f804f49be7a7d701e8f0e46c856d801270b470820a37b + md5: aea31d2e5b1091feca96fcfe945c3cf9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 429011 + timestamp: 1752159441324 - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_0.conda sha256: 7b6316abfea1007e100922760e9b8c820d6fc19df3f42fb5aca684cfacb31843 md5: f9bbae5e2537e3b06e0f7310ba76c893 @@ -2605,9 +5807,9 @@ packages: purls: [] size: 279176 timestamp: 1752159543911 -- conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_9.conda - sha256: 373f2973b8a358528b22be5e8d84322c165b4c5577d24d94fd67ad1bb0a0f261 - md5: 08bfa5da6e242025304b206d152479ef +- conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda + sha256: 0fccf2d17026255b6e10ace1f191d0a2a18f2d65088fd02430be17c701f8ffe0 + md5: 8a86073cf3b343b87d03f41790d8b4e5 depends: - ucrt constrains: @@ -2615,8 +5817,22 @@ packages: - msys2-conda-epoch <0.0a0 license: MIT AND BSD-3-Clause-Clear purls: [] - size: 35794 - timestamp: 1737099561703 + size: 36621 + timestamp: 1759768399557 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa + md5: 92ed62436b625154323d40d5f2f11dd7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + purls: [] + size: 395888 + timestamp: 1727278577118 - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda sha256: 08dec73df0e161c96765468847298a420933a36bc4f09b50e062df8793290737 md5: a69bbf778a462da324489976c84cfc8c @@ -2632,11 +5848,56 @@ packages: purls: [] size: 1208687 timestamp: 1727279378819 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.13.8-h741aa76_1.conda - sha256: 32fa908bb2f2a6636dab0edaac1d4bf5ff62ad404a82d8bb16702bc5b8eb9114 - md5: aeb49dc1f5531de13d2c0d57ffa6d0c8 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c + md5: 5aa797f8787fe7a17d1b0821485b5adc depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + purls: [] + size: 100393 + timestamp: 1702724383534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.0-hca5e8e5_0.conda + sha256: 576ce5378cc6a2b722ff33d2359ccb74dea1e6465daa45116e57550f1eb4ba7e + md5: aa65b4add9574bb1d23c76560c5efd4c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxcb >=1.17.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - xkeyboard-config + - xorg-libxau >=1.0.12,<2.0a0 + license: MIT/X11 Derivative + license_family: MIT + purls: [] + size: 843995 + timestamp: 1762341607312 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda + sha256: ec0735ae56c3549149eebd7dc22c0bed91fd50c02eaa77ff418613ddda190aa8 + md5: e512be7dc1f84966d50959e900ca121f + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libxml2-16 2.15.1 ha9997c6_0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 45283 + timestamp: 1761015644057 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-ha29bfb0_0.conda + sha256: fb51b91a01eac9ee5e26c67f4e081f09f970c18a3da5231b8172919a1e1b3b6b + md5: 87116b9de9c1825c3fd4ef92c984877b + depends: + - icu >=75.1,<76.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libxml2-16 2.15.1 h06f855e_0 - libzlib >=1.3.1,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 @@ -2644,21 +5905,84 @@ packages: license: MIT license_family: MIT purls: [] - size: 1519401 - timestamp: 1754315497781 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h25c3957_0.conda - sha256: 20857f1adb91cc59826e146ee6cb1157c6abf2901a93d359b1106ba87c8e770b - md5: e84f36aa02735c140099d992d491968d + size: 43042 + timestamp: 1761016261024 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda + sha256: 71436e72a286ef8b57d6f4287626ff91991eb03c7bdbe835280521791efd1434 + md5: e7733bc6785ec009e47a224a71917e84 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - libxml2 2.15.1 + license: MIT + license_family: MIT + purls: [] + size: 556302 + timestamp: 1761015637262 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.1-h06f855e_0.conda + sha256: 3f65ea0f04c7738116e74ca87d6e40f8ba55b3df31ef42b8cb4d78dd96645e90 + md5: 4a5ea6ec2055ab0dfd09fd0c498f834a depends: - - libxml2 >=2.13.8,<2.14.0a0 + - icu >=75.1,<76.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libzlib >=1.3.1,<2.0a0 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - libxml2 2.15.1 + license: MIT + license_family: MIT + purls: [] + size: 518616 + timestamp: 1761016240185 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + sha256: 0694760a3e62bdc659d90a14ae9c6e132b525a7900e59785b18a08bb52a5d7e5 + md5: 87e6096ec6d542d1c1f8b33245fe8300 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxml2 + - libxml2-16 >=2.14.6 license: MIT license_family: MIT purls: [] - size: 416974 - timestamp: 1753273998632 + size: 245434 + timestamp: 1757963724977 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h0fbe4c1_1.conda + sha256: 13da38939c2c20e7112d683ab6c9f304bfaf06230a2c6a7cf00359da1a003ec7 + md5: 46034d9d983edc21e84c0b36f1b4ba61 + depends: + - libxml2 + - libxml2-16 >=2.14.6 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + purls: [] + size: 420223 + timestamp: 1757963935611 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzip-1.11.2-h6991a6a_0.conda + sha256: 991e7348b0f650d495fb6d8aa9f8c727bdf52dabf5853c0cc671439b160dce48 + md5: a7b27c075c9b7f459f1c022090697cba + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 109043 + timestamp: 1730442108429 - conda: https://conda.anaconda.org/conda-forge/win-64/libzip-1.11.2-h3135430_0.conda sha256: 8ed49d8aa0ff908e16c82f92154174027c8906429e8b63d71f0b27ecc987b43e md5: 09066edc7810e4bd1b41ad01a6cc4706 @@ -2674,6 +5998,19 @@ packages: purls: [] size: 146856 timestamp: 1730442305774 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 + md5: edb0dca6bc32e4f4789199455a1dbeb8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + purls: [] + size: 60963 + timestamp: 1727963148474 - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda sha256: ba945c6493449bed0e6e29883c4943817f7c79cbff52b83360f7b341277c6402 md5: 41fbfac52c601159df6c01f875de31b9 @@ -2688,21 +6025,31 @@ packages: purls: [] size: 55476 timestamp: 1727963768015 -- conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-20.1.8-hfa2b4ca_2.conda - sha256: 8970b7f9057a1c2c18bfd743c6f5ce73b86197d7724423de4fa3d03911d5874b - md5: 2dc2edf349464c8b83a576175fc2ad42 +- conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-21.1.6-h4922eb0_0.conda + sha256: d7b534285d4abe0042ca985149df4888e808a5c1731f4a87c5552dc725d8a1d8 + md5: 7a0b9ce502e0ed62195e02891dfcd704 depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 + - __glibc >=2.17,<3.0.a0 constrains: + - openmp 21.1.6|21.1.6.* - intel-openmp <0.0a0 - - openmp 20.1.8|20.1.8.* license: Apache-2.0 WITH LLVM-exception license_family: APACHE purls: [] - size: 344490 - timestamp: 1756145011384 + size: 3209134 + timestamp: 1763529474187 +- conda: https://conda.anaconda.org/conda-forge/noarch/loguru-0.7.3-pyh707e725_0.conda + sha256: e4a07f357a4cf195a2345dabd98deab80f4d53574abe712a9cc7f22d3f2cc2c3 + md5: 49647ac1de4d1e4b49124aedf3934e02 + depends: + - __unix + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/loguru?source=hash-mapping + size: 59696 + timestamp: 1746634858826 - conda: https://conda.anaconda.org/conda-forge/noarch/loguru-0.7.3-pyh7428d3b_0.conda sha256: 647ce9601c1a63af4710a2d718a74fa521da28262c41bbf86189f6c0906b23f6 md5: a6c25c54d8d524735db2e5785aec0a4e @@ -2717,11 +6064,29 @@ packages: - pkg:pypi/loguru?source=hash-mapping size: 60119 timestamp: 1746634872414 -- conda: https://conda.anaconda.org/conda-forge/win-64/lxml-6.0.1-py310h4b5876d_0.conda - sha256: c77e99d4964e905012d1d56fcc32d6a7d24660ffe60c37ea709753983831b8d4 - md5: b68820e2ee5da106037a32434a860a04 - depends: - - libxml2 >=2.13.8,<2.14.0a0 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lxml-6.0.2-py310he6d4be0_2.conda + sha256: 7c40fdb66f8fea78049a4d175a12d0c46bb7fb8d3ca4bdcc6d9064de818b5ff5 + md5: 4c8d8b7a0e2f506d4187d4a810595bd9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - libxslt >=1.1.43,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: BSD-3-Clause and MIT-CMU + purls: + - pkg:pypi/lxml?source=hash-mapping + size: 1535309 + timestamp: 1762506361626 +- conda: https://conda.anaconda.org/conda-forge/win-64/lxml-6.0.2-py310h095aac5_2.conda + sha256: e22a7c9ffe810d7a8b57289a72f0f1b7774d9b6bc7a6b4dcdb9ba75bf4cf1ab3 + md5: 3a6d7d46ee23f92eb9eff616971f8934 + depends: + - libxml2 + - libxml2-16 >=2.14.6 - libxslt >=1.1.43,<2.0a0 - libzlib >=1.3.1,<2.0a0 - python >=3.10,<3.11.0a0 @@ -2732,8 +6097,20 @@ packages: license: BSD-3-Clause and MIT-CMU purls: - pkg:pypi/lxml?source=hash-mapping - size: 1202559 - timestamp: 1755886182576 + size: 1204704 + timestamp: 1762506596873 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + sha256: 47326f811392a5fd3055f0f773036c392d26fdb32e4d8e7a8197eed951489346 + md5: 9de5350a85c4a20c685259b889aa6393 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 167055 + timestamp: 1733741040117 - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h2466b09_1.conda sha256: 632cf3bdaf7a7aeb846de310b6044d90917728c73c77f138f08aa9438fc4d6b5 md5: 0b69331897a92fac3d8923549d48d092 @@ -2746,40 +6123,71 @@ packages: purls: [] size: 139891 timestamp: 1733741168264 -- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.8.2-pyhd8ed1ab_0.conda - sha256: d495279d947e01300bfbc124859151be4eec3a088c1afe173323fd3aa89423b2 - md5: b0404922d0459f188768d1e613ed8a87 +- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda + sha256: 32af5d32e3193b7c0ea02c33cc8753bfc0965d07e1aa58418a851d0bb94a7792 + md5: 934afb77580165027b869d4104ee002f depends: - importlib-metadata >=4.4 - - python >=3.9 + - python >=3.10 + - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markdown?source=hash-mapping - size: 80353 - timestamp: 1750360406187 -- conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.2-py310h38315fa_1.conda - sha256: deb8505b7ef76d363174d133e2ff814ae75b91ac4c3ae5550a7686897392f4d0 - md5: 79dfc050ae5a7dd4e63e392c984e2576 + size: 85401 + timestamp: 1762856570927 +- conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py310h3406613_0.conda + sha256: b3894b37cab530d1adab5b9ce39a1b9f28040403cc0042b77e04a2f227a447de + md5: 8854df4fb4e37cc3ea0a024e48c9c180 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping + size: 23673 + timestamp: 1759055396627 +- conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py310hdb0e946_0.conda + sha256: 87203ea8bbe265ebabb16673c9442d2097e1b405dc70df49d6920730e7be6e74 + md5: 1fdd2255424eaf0d5e707c205ace2c30 depends: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 constrains: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markupsafe?source=hash-mapping - size: 25941 - timestamp: 1733220087179 -- conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.5-py310h5588dad_0.conda - sha256: fc2fa21745542c99e85889432fe2e2a62c6fa64b85a8da7a473ddd6404dd715a - md5: b20be645a9630ef968db84bdda3aa716 + size: 26586 + timestamp: 1759055463355 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.8-py310hff52083_0.conda + sha256: 6d087ae3f42e5a53f648a874629b561e8ec34416f6a258837ca0af405550defe + md5: e78bcae4f58d0000f756c3b42da20f13 + depends: + - matplotlib-base >=3.10.8,<3.10.9.0a0 + - pyside6 >=6.7.2 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - tornado >=5 + license: PSF-2.0 + license_family: PSF + purls: [] + size: 17450 + timestamp: 1763055406857 +- conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.8-py310h5588dad_0.conda + sha256: 277b0e73a023978311fff8976b6b92e529b13dc9d4487414e12695f5ee0d8555 + md5: 178a19a3e53ec4d213f1193c34e92500 depends: - - matplotlib-base >=3.10.5,<3.10.6.0a0 + - matplotlib-base >=3.10.8,<3.10.9.0a0 - pyside6 >=6.7.2 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 @@ -2787,19 +6195,49 @@ packages: license: PSF-2.0 license_family: PSF purls: [] - size: 17850 - timestamp: 1754006159977 -- conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.5-py310h0bdd906_0.conda - sha256: 788c18ad973498ac078da8c99c6429a905a8fc49fa90b19ed7895a9f048db436 - md5: a26309db5dc93b40f5e6bf69187f631e + size: 17874 + timestamp: 1763055525619 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py310hfde16b3_0.conda + sha256: 809eaf93eb1901764c9b75803794c0359dd09366f578a13fdbbbe99824920d2c + md5: 093b60a14d2c0d8c10f17e14a73a60d3 depends: + - __glibc >=2.17,<3.0.a0 - contourpy >=1.0.1 - cycler >=0.10 - fonttools >=4.22.0 - freetype - kiwisolver >=1.3.1 - - libfreetype >=2.13.3 - - libfreetype6 >=2.13.3 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libstdcxx >=14 + - numpy >=1.21,<3 + - numpy >=1.23 + - packaging >=20.0 + - pillow >=8 + - pyparsing >=2.3.1 + - python >=3.10,<3.11.0a0 + - python-dateutil >=2.7 + - python_abi 3.10.* *_cp310 + - qhull >=2020.2,<2020.3.0a0 + - tk >=8.6.13,<8.7.0a0 + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/matplotlib?source=hash-mapping + size: 7273307 + timestamp: 1763055380888 +- conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.8-py310h0bdd906_0.conda + sha256: 97bf5dcb9c38031ff55fa8b92c872a49938e264b0670d1889118eaca72de4b9e + md5: 13072a2da6b67737ad24e22041f68ef5 + depends: + - contourpy >=1.0.1 + - cycler >=0.10 + - fonttools >=4.22.0 + - freetype + - kiwisolver >=1.3.1 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 - numpy >=1.21,<3 - numpy >=1.23 - packaging >=20.0 @@ -2816,8 +6254,8 @@ packages: license_family: PSF purls: - pkg:pypi/matplotlib?source=hash-mapping - size: 7164379 - timestamp: 1754006122575 + size: 7166182 + timestamp: 1763055495695 - conda: https://conda.anaconda.org/conda-forge/noarch/mergedeep-1.3.4-pyhd8ed1ab_1.conda sha256: e5b555fd638334a253d83df14e3c913ef8ce10100090e17fd6fb8e752d36f95d md5: d9a8fc1f01deae61735c88ec242e855c @@ -2887,19 +6325,19 @@ packages: - pkg:pypi/mkdocs-get-deps?source=hash-mapping size: 14757 timestamp: 1734353035244 -- pypi: https://files.pythonhosted.org/packages/53/b6/106fcc15287e7228658fbd0ad9e8b0d775becced0a089cc39984641f4a0f/mkdocs_git_revision_date_localized_plugin-1.4.7-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/bc/51/fe0e3fdb16f6eed65c9459d12bae6a4e1f0bb4e2228cb037e7907b002678/mkdocs_git_revision_date_localized_plugin-1.5.0-py3-none-any.whl name: mkdocs-git-revision-date-localized-plugin - version: 1.4.7 - sha256: 056c0a90242409148f1dc94d5c9d2c25b5b8ddd8de45489fa38f7fa7ccad2bc4 + version: 1.5.0 + sha256: 933f9e35a8c135b113f21bb57610d82e9b7bcc71dd34fb06a029053c97e99656 requires_dist: - babel>=2.7.0 - gitpython>=3.1.44 - mkdocs>=1.0 - - pytz>=2025.1 - requires_python: '>=3.8' -- conda: https://conda.anaconda.org/conda-forge/noarch/mkdocs-material-9.6.18-pyhcf101f3_0.conda - sha256: 0b1d1cada3e48685e5813e6e39862bc3cccf91cde97483f01e938764693c2fcc - md5: 11cff74f181a9a348425704b20ca4d40 + - tzdata>=2023.3 ; sys_platform == 'win32' + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/noarch/mkdocs-material-9.7.0-pyhcf101f3_0.conda + sha256: c14cfff88be4ea779071f1ddb9c1944fa7d2a2c7f9ea09b8e0b9f5b84a70e6c1 + md5: 0ee60d02b6ffe184d355e1318d7e9207 depends: - python >=3.10 - jinja2 >=3.0,<4.dev0 @@ -2918,8 +6356,8 @@ packages: license_family: MIT purls: - pkg:pypi/mkdocs-material?source=hash-mapping - size: 4738235 - timestamp: 1755878847003 + size: 4793171 + timestamp: 1762865411798 - conda: https://conda.anaconda.org/conda-forge/noarch/mkdocs-material-extensions-1.3.1-pyhd8ed1ab_1.conda sha256: f62955d40926770ab65cc54f7db5fde6c073a3ba36a0787a7a5767017da50aa3 md5: de8af4000a4872e16fb784c649679c8e @@ -2933,10 +6371,10 @@ packages: - pkg:pypi/mkdocs-material-extensions?source=hash-mapping size: 16122 timestamp: 1734641109286 -- pypi: https://files.pythonhosted.org/packages/8e/d5/15f6eeeb755e57a501fad6dcfb3fe406dea5f6a6347a77c3be114294f7bb/mkdocs_mermaid2_plugin-1.2.2-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/1a/4b/6fd6dd632019b7f522f1b1f794ab6115cd79890330986614be56fd18f0eb/mkdocs_mermaid2_plugin-1.2.3-py3-none-any.whl name: mkdocs-mermaid2-plugin - version: 1.2.2 - sha256: a003dddd6346ecc0ad530f48f577fe6f8b21ea23fbee09eabf0172bbc1f23df8 + version: 1.2.3 + sha256: 33f60c582be623ed53829a96e19284fc7f1b74a1dbae78d4d2e47fe00c3e190d requires_dist: - beautifulsoup4>=4.6.3 - jsbeautifier @@ -2949,17 +6387,45 @@ packages: - packaging ; extra == 'test' - requests-html ; extra == 'test' requires_python: '>=3.8' -- conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2024.2.2-h57928b3_16.conda - sha256: ce841e7c3898764154a9293c0f92283c1eb28cdacf7a164c94b632a6af675d91 - md5: 5cddc979c74b90cf5e5cda4f97d5d8bb +- conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2025.3.0-h0e700b2_462.conda + sha256: b5ddfb4378c19d0d69e751478a7733dee035d1dd1f206e7a88a5df4ee71345e0 + md5: a2e8e73f7132ea5ea70fda6f3cf05578 depends: - - llvm-openmp >=20.1.8 + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex * *_llvm + - _openmp_mutex >=4.5 + - libgcc >=14 + - libstdcxx >=14 + - llvm-openmp >=21.1.4 + - tbb >=2022.2.0 + license: LicenseRef-IntelSimplifiedSoftwareOct2022 + license_family: Proprietary + purls: [] + size: 125177250 + timestamp: 1761668323993 +- conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2024.2.2-h57928b3_15.conda + sha256: 592e17e20bb43c3e30b58bb43c9345490a442bff1c6a6236cbf3c39678f915af + md5: 5d760433dc75df74e8f9ede69d11f9ec + depends: + - intel-openmp 2024.* - tbb 2021.* license: LicenseRef-IntelSimplifiedSoftwareOct2022 license_family: Proprietary purls: [] - size: 103088799 - timestamp: 1753975600547 + size: 102928701 + timestamp: 1753396273118 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.9-hc50e24c_0.conda + sha256: 39c4700fb3fbe403a77d8cc27352fa72ba744db487559d5d44bf8411bb4ea200 + md5: c7f302fd11eeb0987a6a5e1f3aed6a21 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: LGPL-2.1-only + license_family: LGPL + purls: [] + size: 491140 + timestamp: 1730581373280 - conda: https://conda.anaconda.org/conda-forge/win-64/mpg123-1.32.9-h01009b0_0.conda sha256: a1d7d25f2c448f5c47d1678cca1f6ae5deadb38e176ea0c76ea5c688589dfd7a md5: 1ed1580d4211223b285787eff05560f9 @@ -2985,21 +6451,51 @@ packages: - sphinx ; extra == 'docs' - gmpy2>=2.1.0a4 ; platform_python_implementation != 'PyPy' and extra == 'gmpy' - pytest>=4.6 ; extra == 'tests' -- conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.1.1-py310hc19bc0b_0.conda - sha256: 83e0bcf2f4cddc3421ad1cff30058ce7100210821b279bd3ad458bfc8c59eefe - md5: 061803553d610adf1c4c545c71aa9a85 +- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py310h03d9f68_1.conda + sha256: 61cf3572d6afa3fa711c5f970a832783d2c281facb7b3b946a6b71a0bac2c592 + md5: 5eea9d8f8fcf49751dab7927cb0dfc3f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/msgpack?source=hash-mapping + size: 95105 + timestamp: 1762504073388 +- conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.1.2-py310he9f1925_1.conda + sha256: 6b7bfd07c5be57df2922e2f5238751ee6bb09d81540a44c6554d059eac2a3bd5 + md5: 65fb9838e245ef4bea6cab32a7056dfc depends: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/msgpack?source=hash-mapping - size: 80891 - timestamp: 1749813752831 + size: 80807 + timestamp: 1762504309629 +- conda: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.6.3-py310h89163eb_0.conda + sha256: e901bdd27b655ce411b1cda04b5ecabad93d4f4fef929ca46e075678fab5fcf5 + md5: 5daf15c40f96f1c2f3721d09e93b66b6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - typing-extensions >=4.1.0 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/multidict?source=hash-mapping + size: 88385 + timestamp: 1751310722566 - conda: https://conda.anaconda.org/conda-forge/win-64/multidict-6.6.3-py310hdb0e946_0.conda sha256: e3f9ac76ce4a852cebcd68f52277095a51cac523de20747cbdbcd6a6b6df2e63 md5: 2c03561bfc027cdd48c093634ea44721 @@ -3027,9 +6523,9 @@ packages: - pkg:pypi/munkres?source=hash-mapping size: 15851 timestamp: 1749895533014 -- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.2.0-pyhcf101f3_0.conda - sha256: 9f08e4e50695546e6c68288a35350b5cce8be13fbd1f4dc0ecf04a1e180e1673 - md5: 7b058c5f94d7fdfde0f91e3f498b81fc +- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.12.0-pyhcf101f3_0.conda + sha256: 1bdb3210db397315c7334c0432a07199b5db28f3c79adcafb6a7436f93423a92 + md5: 02cab382663872083b7e8675f09d9c21 depends: - python >=3.10 - python @@ -3037,8 +6533,18 @@ packages: license_family: MIT purls: - pkg:pypi/narwhals?source=compressed-mapping - size: 248742 - timestamp: 1756119139962 + size: 267826 + timestamp: 1763381298763 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586 + md5: 47e340acb35de30501a76c7c799c41d7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: X11 AND BSD-3-Clause + purls: [] + size: 891641 + timestamp: 1738195959188 - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda sha256: bb7b21d7fd0445ddc0631f64e66d91a179de4ba920b8381f29b9d006a42788c0 md5: 598fd7d4d0de2455fb74f56063969a97 @@ -3067,14 +6573,46 @@ packages: - pkg:pypi/networkx?source=hash-mapping size: 1265008 timestamp: 1731521053408 -- conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-he0c23c2_0.conda - sha256: 046a033594e87705de4edab215ceb567ea24e205fbd058d3fbfd7055b8a80fa4 - md5: 401617c1ad869b46966165aba18466fd +- conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda + sha256: fd2cbd8dfc006c72f45843672664a8e4b99b2f8137654eaae8c3d46dca776f63 + md5: 16c2a0e9c4a166e53632cfca4f68d020 + constrains: + - nlohmann_json-abi ==3.12.0 license: MIT license_family: MIT purls: [] - size: 134432 - timestamp: 1744445192270 + size: 136216 + timestamp: 1758194284857 +- conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_1.conda + sha256: 045edd5d571c235de67472ad8fe03d9706b8426c4ba9a73f408f946034b6bc5e + md5: 24a9dde77833cc48289ef92b4e724da4 + constrains: + - nlohmann_json-abi ==3.12.0 + license: MIT + license_family: MIT + purls: [] + size: 134870 + timestamp: 1758194302226 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.6-py310hefbff90_0.conda + sha256: 0ba94a61f91d67413e60fa8daa85627a8f299b5054b0eff8f93d26da83ec755e + md5: b0cea2c364bf65cd19e023040eeab05d + depends: + - __glibc >=2.17,<3.0.a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc >=13 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx >=13 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping + size: 7893263 + timestamp: 1747545075833 - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.2.6-py310h4987827_0.conda sha256: 6f628e51763b86a535a723664e3aa1e38cb7147a2697f80b75c1980c1ed52f3e md5: d2596785ac2cf5bab04e2ee9e5d04041 @@ -3095,6 +6633,102 @@ packages: - pkg:pypi/numpy?source=hash-mapping size: 6596153 timestamp: 1747545352390 +- pypi: https://files.pythonhosted.org/packages/5a/99/210e113dde53955e97042bd76dc4ad927eca04c5b4645ec157cc59f4f3ae/nvidia_cublas-13.0.0.19-py3-none-manylinux_2_27_x86_64.whl + name: nvidia-cublas + version: 13.0.0.19 + sha256: f6723af2e8e2600a11dc384037d90d9bf93070e346c24ef2e8f9001658c99896 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/ba/28/e37d62ff27b4462953fdd5713d8a78760578dfa12685c30b71b55fab57b1/nvidia_cuda_cupti-13.0.48-py3-none-manylinux_2_25_x86_64.whl + name: nvidia-cuda-cupti + version: 13.0.48 + sha256: 417699e216b23d81bc0bbcb7032352f81b9c5372ef73c097a01abb83125a3d09 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/be/5b/f7636b3d66caefade6a0a0dc5b705c259a2062c20ad18b432b3129d348e0/nvidia_cuda_nvrtc-13.0.48-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl + name: nvidia-cuda-nvrtc + version: 13.0.48 + sha256: 87e13d186905a35e7c04ad553a2abded0fba22f93b43d02e5da6f6cf73fb4d0a + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/cc/78/edb119083ca2ff0f09ab0cd597e97775ac3f575b8aa0caf10d68ed49e032/nvidia_cuda_runtime-13.0.48-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: nvidia-cuda-runtime + version: 13.0.48 + sha256: 5b54d12087a1abff81a4cbfa6556876e3afea1fc60da2e0816da374619810c89 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/cf/68/2712854561170b2a81bea7b6b35cc1ae264d9794c0c218986e5c685d45f7/nvidia_cudnn_cu13-9.13.0.50-py3-none-manylinux_2_27_x86_64.whl + name: nvidia-cudnn-cu13 + version: 9.13.0.50 + sha256: 2150b4850725d30653ec3e365f0732e3e2e3eb8633cf3bd2d3117628dea8b4f9 + requires_dist: + - nvidia-cublas + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/9b/9f/e298b66e584ad25bd78ad4a45b061fe7bb57a1ec011128089404ce3fcc7d/nvidia_cufft-12.0.0.15-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: nvidia-cufft + version: 12.0.0.15 + sha256: 9f160b1f018e80bcb0d7c0fa50564b042fa26b13edc1b1ff14b6375a9edd2812 + requires_dist: + - nvidia-nvjitlink + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/ad/0a/4adf0c9bb1241cd1314fc923fde00f3749c7fc785b1e3b3f4a104cd3090c/nvidia_cufile-1.15.0.42-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: nvidia-cufile + version: 1.15.0.42 + sha256: c8f9813eff24d61586699c615e39817e2b4e4f642cace32733c2ab6f663a7eab + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl + name: nvidia-curand + version: 10.4.0.35 + sha256: 1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/a5/87/e3c9ee227b750e5b61572e7509f586cc8d494a4f7874b5163e734ed852c2/nvidia_cusolver-12.0.3.29-py3-none-manylinux_2_27_x86_64.whl + name: nvidia-cusolver + version: 12.0.3.29 + sha256: 6f54c2eed5edab54c224dd1852dde80ba76b2b78e6d3ce7344fef5dfc66d16ab + requires_dist: + - nvidia-cublas + - nvidia-nvjitlink + - nvidia-cusparse + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/ba/e8/b3f7a87cc719dca926c7baee92f2544de8909573a4126c85a9f1625431e8/nvidia_cusparse-12.6.2.49-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: nvidia-cusparse + version: 12.6.2.49 + sha256: efcf0b01e3a0827c144feff5391456b8a06e9ce63dcd51c0943e32e605251952 + requires_dist: + - nvidia-nvjitlink + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl + name: nvidia-cusparselt-cu13 + version: 0.8.0 + sha256: 25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd +- pypi: https://files.pythonhosted.org/packages/f1/3a/dabb10684e60edfaf1a1c9984d12a668bc1091582099d4e03ac5b9983b51/nvidia_nccl_cu13-2.27.7-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: nvidia-nccl-cu13 + version: 2.27.7 + sha256: b28a524abd8389b76a4a3f133c76a7aaa7005e47fcaa9d9603b90103927a3f93 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/95/39/726edebeb76f3efc25c79f885429fa1227c9d200e20ea219bf724b382e19/nvidia_nvjitlink-13.0.39-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl + name: nvidia-nvjitlink + version: 13.0.39 + sha256: bc3179be558329ef9687884c6faa27cdc0659bdbc642432ec8cc6cc00d182627 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/6f/e9/8530afb8ed38d16bbc89cec80a4dd6a52dbf59bc93e546c3658cfa8b1f9b/nvidia_nvshmem_cu13-3.3.24-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: nvidia-nvshmem-cu13 + version: 3.3.24 + sha256: c14d09571697d2e57cb079c8daec88ab1c68cb3586532bfbd4886125a08339b7 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/38/37/0d103c84e7884382a79a569b720965141f83dd1c5df9e3e00cbc02d7099c/nvidia_nvtx-13.0.39-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl + name: nvidia-nvtx + version: 13.0.39 + sha256: cc113127785c96db8a0fe715df92db9788777b4b3d1bd713d42f75969201b5ce + requires_python: '>=3' +- conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda + sha256: 2254dae821b286fb57c61895f2b40e3571a070910fdab79a948ff703e1ea807b + md5: 56f8947aa9d5cf37b0b3d43b83f34192 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - opencl-headers >=2024.10.24 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 106742 + timestamp: 1743700382939 - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda sha256: df806841be847e5287b22b6ae7f380874f81ea51f1b51ae14a570f3385c7b133 md5: 23cc056834cab53849b91f78d6ee3ea0 @@ -3109,15 +6743,59 @@ packages: - pkg:pypi/omegaconf?source=hash-mapping size: 166453 timestamp: 1670575519562 -- conda: https://conda.anaconda.org/conda-forge/win-64/open3d-0.19.0-py310hadc9e84_4.conda - sha256: 926b6e5828a2e075166c2524a4ebb2333c3fb9805ff6ffee26797c2de688368c - md5: 2977e48f42b4385abd70dfb70263f773 +- conda: https://conda.anaconda.org/conda-forge/linux-64/open3d-0.19.0-py310h757a272_6.conda + sha256: 51338c54e2dd41bf33296cda08106f52d6f8094a4a52f6c4b9f9535651d36881 + md5: d9ae4017636af4c6bab3bf0992b4ba2d + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - assimp >=5.4.3,<5.4.4.0a0 + - dash + - embree >=4.4.0,<4.5.0a0 + - fmt >=11.2.0,<11.3.0a0 + - glew >=2.2.0,<2.3.0a0 + - glfw >=3.4,<4.0a0 + - jsoncpp >=1.9.6,<1.9.7.0a0 + - libcurl >=8.14.1,<9.0a0 + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libglx >=1.7.0,<2.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblapacke >=3.9.0,<4.0a0 + - liblzf >=3.6,<3.7.0a0 + - libopengl >=1.7.0,<2.0a0 + - libpng >=1.6.50,<1.7.0a0 + - libstdcxx >=14 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - numpy + - plotly + - pybind11-abi 4 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - qhull >=2020.2,<2020.3.0a0 + - qt6-main >=6.9.3,<6.10.0a0 + - tbb >=2021.13.0 + - tinyobjloader >=1.0.7,<2.0a0 + - vtk-base >=9.5.1,<9.5.2.0a0 + - xorg-libxdamage >=1.1.6,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + - zeromq >=4.3.5,<4.4.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/open3d-cpu?source=hash-mapping + size: 10276646 + timestamp: 1760688630569 +- conda: https://conda.anaconda.org/conda-forge/win-64/open3d-0.19.0-py310hc9aaaf8_6.conda + sha256: 43aaa08a2ab72c98b3a0789d54ad4c25abe8e63ea60cf7cca5174a26c7bac418 + md5: 158dba9bdeaec4b77d79ca2260e67f58 depends: - assimp >=5.4.3,<5.4.4.0a0 - dash - embree >=4.4.0,<4.5.0a0 - fmt >=11.2.0,<11.3.0a0 - - glew >=2.1.0,<2.2.0a0 + - glew >=2.2.0,<2.3.0a0 - glfw >=3.4,<4.0a0 - jsoncpp >=1.9.6,<1.9.7.0a0 - libblas * *mkl @@ -3126,30 +6804,43 @@ packages: - liblapacke >=3.9.0,<3.10.0a0 - liblzf >=3.6,<3.7.0a0 - liblzma >=5.8.1,<6.0a0 - - libpng >=1.6.49,<1.7.0a0 + - libpng >=1.6.50,<1.7.0a0 - libzlib >=1.3.1,<2.0a0 - minizip - mkl >=2024.2.2,<2025.0a0 - numpy - - openssl >=3.5.0,<4.0a0 + - openssl >=3.5.4,<4.0a0 - plotly + - pybind11-abi 4 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - qhull >=2020.2,<2020.3.0a0 - - qt6-main >=6.9.1,<6.10.0a0 + - qt6-main >=6.9.3,<6.10.0a0 - tbb >=2021.13.0 - tinyobjloader >=1.0.7,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - - vtk-base >=9.4.2,<9.4.3.0a0 + - vtk-base >=9.5.1,<9.5.2.0a0 - zeromq >=4.3.5,<4.3.6.0a0 license: MIT license_family: MIT purls: - pkg:pypi/open3d?source=hash-mapping - size: 14296389 - timestamp: 1751368072743 + size: 14317668 + timestamp: 1760690599710 +- conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda + sha256: 2b6ce54174ec19110e1b3c37455f7cd138d0e228a75727a9bba443427da30a36 + md5: 45c3d2c224002d6d0d7769142b29f986 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 55357 + timestamp: 1749853464518 - conda: https://conda.anaconda.org/conda-forge/win-64/opencl-headers-2025.06.13-he0c23c2_0.conda sha256: 1958dd489d32c3635e411e1802607e04a42ec685f1b2d63292211383447cecd3 md5: 25b288eda332180bba67ef785a20ae45 @@ -3162,22 +6853,51 @@ packages: purls: [] size: 55411 timestamp: 1749853655608 -- conda: https://conda.anaconda.org/conda-forge/win-64/opencv-4.12.0-qt6_py310he543868_603.conda - sha256: 6233b3fcc1075cd7c698431ae1edf57eac27390a737179dcc273273584d7f35e - md5: 466afd8d14921a3b7d03258c8384d9c1 +- conda: https://conda.anaconda.org/conda-forge/linux-64/opencv-4.12.0-qt6_py310hec850f8_607.conda + sha256: aa78654d9027bc2d2448f37b211fa3b71c480f72a71c439bec57f8d1b9693266 + md5: 52a7359e571ada352a9fff527249ba0d depends: - - libopencv 4.12.0 qt6_py310h5bb264c_603 + - libopencv 4.12.0 qt6_py310hdb0ca46_607 - libprotobuf >=6.31.1,<6.31.2.0a0 - - py-opencv 4.12.0 qt6_py310h9217539_603 + - py-opencv 4.12.0 qt6_py310h89973df_607 - python_abi 3.10.* *_cp310 license: Apache-2.0 license_family: Apache purls: [] - size: 27465 - timestamp: 1755994847847 -- conda: https://conda.anaconda.org/conda-forge/win-64/openexr-3.3.5-hed76565_1.conda - sha256: 053da2b52e0cf0b200222e94803951bb339c514e1f79d343e95ef117ab0b6708 - md5: 4c5cd5fc071a7d5fb4889402d262ec7f + size: 27731 + timestamp: 1760195304450 +- conda: https://conda.anaconda.org/conda-forge/win-64/opencv-4.12.0-qt6_py310he543868_607.conda + sha256: 64d80837a7fd5d4d4ea7680416f3702e46f99aa358e56a44c93d62d47875b65c + md5: 121effd812d567c325aa171f918dda4f + depends: + - libopencv 4.12.0 qt6_py310h3c58159_607 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - py-opencv 4.12.0 qt6_py310h9217539_607 + - python_abi 3.10.* *_cp310 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 28010 + timestamp: 1760196469234 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openexr-3.4.4-he10986b_0.conda + sha256: 72a619bfa153f6638899d22a4b7ae87e96937008953946b2beffdacd46bf5875 + md5: 87a5d17408628edc66b54822da1d29da + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + - imath >=3.2.2,<3.2.3.0a0 + - libdeflate >=1.25,<1.26.0a0 + - openjph >=0.25.3,<0.26.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1219208 + timestamp: 1763615175237 +- conda: https://conda.anaconda.org/conda-forge/win-64/openexr-3.4.4-ha75af49_0.conda + sha256: 871f1c9a666f6077d0ac52908dd720108dfc133f98fb3fe64554979163ef88a8 + md5: 83318300cb9803abf7b45cb88c169e19 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -3185,14 +6905,27 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - libdeflate >=1.24,<1.25.0a0 - - imath >=3.2.1,<3.2.2.0a0 + - libdeflate >=1.25,<1.26.0a0 + - openjph >=0.25.3,<0.26.0a0 + - imath >=3.2.2,<3.2.3.0a0 - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] - size: 1060477 - timestamp: 1755533947968 + size: 948094 + timestamp: 1763615187289 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openh264-2.6.0-hc22cd8d_0.conda + sha256: 3f231f2747a37a58471c82a9a8a80d92b7fece9f3fce10901a5ac888ce00b747 + md5: b28cf020fd2dead0ca6d113608683842 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 731471 + timestamp: 1739400677213 - conda: https://conda.anaconda.org/conda-forge/win-64/openh264-2.6.0-hb17fa0b_0.conda sha256: 914702d9a64325ff3afb072c8bc0f8cbea3f19955a8395a8c190e45604f83c76 md5: ad4cac6ceb9e4c8e01802e3f15e87bb2 @@ -3205,12 +6938,27 @@ packages: purls: [] size: 411269 timestamp: 1739401120354 -- conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.3-h24db6dd_1.conda - sha256: c29cb1641bc5cfc2197e9b7b436f34142be4766dd2430a937b48b7474935aa55 - md5: 25f45acb1a234ad1c9b9a20e1e6c559e +- conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + sha256: 3900f9f2dbbf4129cf3ad6acf4e4b6f7101390b53843591c53b00f034343bc4d + md5: 11b3379b191f63139e29c0d19dee24cd depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 - libpng >=1.6.50,<1.7.0a0 - - libtiff >=4.7.0,<4.8.0a0 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 355400 + timestamp: 1758489294972 +- conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h24db6dd_0.conda + sha256: 226c270a7e3644448954c47959c00a9bf7845f6d600c2a643db187118d028eee + md5: 5af852046226bb3cb15c7f61c2ac020a + depends: + - libpng >=1.6.50,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 - libzlib >=1.3.1,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 @@ -3218,11 +6966,68 @@ packages: license: BSD-2-Clause license_family: BSD purls: [] - size: 245076 - timestamp: 1754298075628 -- conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.5.2-h725018a_0.conda - sha256: 2413f3b4606018aea23acfa2af3c4c46af786739ab4020422e9f0c2aec75321b - md5: 150d3920b420a27c0848acca158f94dc + size: 244860 + timestamp: 1758489556249 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openjph-0.25.3-h8d634f6_0.conda + sha256: e5d5907f6de5322975fdef4f38897f3eb75e74ec50efdaea7142cbd0ac638f19 + md5: 2fba370596b7833c3b5e2d626da21ae2 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - libtiff >=4.7.1,<4.8.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 279814 + timestamp: 1763316134905 +- conda: https://conda.anaconda.org/conda-forge/win-64/openjph-0.25.3-hf13a347_0.conda + sha256: 3f59478860e5c00eabb4b8998c4eadebcbc4640bbb56ebaa5abfe14eb2322351 + md5: c084115112e4fbefd921c53854e7cb4c + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libtiff >=4.7.1,<4.8.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 215012 + timestamp: 1763316168042 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda + sha256: cb0b07db15e303e6f0a19646807715d28f1264c6350309a559702f4f34f37892 + md5: 2e5bf4f1da39c0b32778561c3c4e5878 + depends: + - __glibc >=2.17,<3.0.a0 + - cyrus-sasl >=2.1.27,<3.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.5.0,<4.0a0 + license: OLDAP-2.8 + license_family: BSD + purls: [] + size: 780253 + timestamp: 1748010165522 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda + sha256: a47271202f4518a484956968335b2521409c8173e123ab381e775c358c67fe6d + md5: 9ee58d5c534af06558933af3c845a780 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 3165399 + timestamp: 1762839186699 +- conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_0.conda + sha256: 6d72d6f766293d4f2aa60c28c244c8efed6946c430814175f959ffe8cab899b3 + md5: 84f8fb4afd1157f59098f618cd2437e4 depends: - ca-certificates - ucrt >=10.0.20348.0 @@ -3231,8 +7036,21 @@ packages: license: Apache-2.0 license_family: Apache purls: [] - size: 9275175 - timestamp: 1754467904482 + size: 9440812 + timestamp: 1762841722179 +- conda: https://conda.anaconda.org/conda-forge/linux-64/opusfile-0.12-h3358134_2.conda + sha256: f4df9df880e405e5c856383f869d5b9d434f78fb7c234c9e7b099ab604fb7fc3 + md5: 5931bcae00b98f952696b6bcdd0be34b + depends: + - libgcc-ng >=12 + - libogg >=1.3.4,<1.4.0a0 + - libopus >=1.3.1,<2.0a0 + - openssl >=3.0.7,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 65901 + timestamp: 1670387479735 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda sha256: 289861ed0c13a15d7bbb408796af4de72c2fe67e2bcb0de98f4c3fce259d7991 md5: 58335b26c38bf4a20f399384c33cbcf9 @@ -3256,10 +7074,13 @@ packages: - pkg:pypi/paginate?source=hash-mapping size: 18865 timestamp: 1734618649164 -- conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.3.2-py310hed136d8_0.conda - sha256: c75d6da34cb2145d874b440eafd4b1c29d13c50d2355fa7fdd2382ad7ccddfb8 - md5: 927ed22167ca78a54b49fb30bb42fa35 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py310h0158d43_1.conda + sha256: cc0935188e132ff9bee7cbed0f81164735ae407d80f4b9cae85b6de2df13e88e + md5: 8bae331f955bac51bacbfb94ad81b7e5 depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 - numpy >=1.21,<3 - numpy >=1.22.4 - python >=3.10,<3.11.0a0 @@ -3267,47 +7088,117 @@ packages: - python-tzdata >=2022.7 - python_abi 3.10.* *_cp310 - pytz >=2020.1 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 constrains: - scipy >=1.10.0 + - sqlalchemy >=2.0.0 + - pyreadstat >=1.2.0 + - blosc >=1.21.3 + - xlsxwriter >=3.0.5 + - pyxlsb >=1.0.10 + - pandas-gbq >=0.19.0 + - lxml >=4.9.2 + - matplotlib >=3.6.3 + - beautifulsoup4 >=4.11.2 + - xlrd >=2.0.1 + - fsspec >=2022.11.0 + - html5lib >=1.1 + - s3fs >=2022.11.0 + - pyarrow >=10.0.1 + - tzdata >=2022.7 + - bottleneck >=1.3.6 + - qtpy >=2.3.0 + - xarray >=2022.12.0 + - openpyxl >=3.1.0 + - gcsfs >=2022.11.0 + - pytables >=3.8.0 - pyqt5 >=5.15.9 + - python-calamine >=0.1.7 + - tabulate >=0.9.0 + - numba >=0.56.4 + - fastparquet >=2022.12.0 + - zstandard >=0.19.0 + - odfpy >=1.4.1 - numexpr >=2.8.4 - psycopg2 >=2.9.6 - - bottleneck >=1.3.6 - - tabulate >=0.9.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pandas?source=hash-mapping + size: 12629957 + timestamp: 1759266070024 +- conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.3.3-py310hed136d8_1.conda + sha256: cc50c3c8921a86e79817e1f206440fba207ffc3ca912685a8ef14484eb7ece62 + md5: 044dd35ee11c344a6471fffca2c857ce + depends: + - numpy >=1.21,<3 + - numpy >=1.22.4 + - python >=3.10,<3.11.0a0 + - python-dateutil >=2.8.2 + - python-tzdata >=2022.7 + - python_abi 3.10.* *_cp310 + - pytz >=2020.1 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - html5lib >=1.1 + - pyreadstat >=1.2.0 + - beautifulsoup4 >=4.11.2 - lxml >=4.9.2 - - pyarrow >=10.0.1 + - qtpy >=2.3.0 - pytables >=3.8.0 - - fastparquet >=2022.12.0 - - numba >=0.56.4 + - xlsxwriter >=3.0.5 + - blosc >=1.21.3 + - pyarrow >=10.0.1 + - bottleneck >=1.3.6 + - sqlalchemy >=2.0.0 + - scipy >=1.10.0 + - pyqt5 >=5.15.9 + - s3fs >=2022.11.0 + - psycopg2 >=2.9.6 - xarray >=2022.12.0 - pyxlsb >=1.0.10 - - blosc >=1.21.3 - - xlsxwriter >=3.0.5 - - pandas-gbq >=0.19.0 - xlrd >=2.0.1 - - sqlalchemy >=2.0.0 - - tzdata >=2022.7 - - openpyxl >=3.1.0 - - odfpy >=1.4.1 - - s3fs >=2022.11.0 - - gcsfs >=2022.11.0 - - qtpy >=2.3.0 - - python-calamine >=0.1.7 - matplotlib >=3.6.3 - - fsspec >=2022.11.0 + - fastparquet >=2022.12.0 + - gcsfs >=2022.11.0 + - pandas-gbq >=0.19.0 - zstandard >=0.19.0 - - pyreadstat >=1.2.0 - - beautifulsoup4 >=4.11.2 - - html5lib >=1.1 + - numba >=0.56.4 + - fsspec >=2022.11.0 + - odfpy >=1.4.1 + - tabulate >=0.9.0 + - python-calamine >=0.1.7 + - numexpr >=2.8.4 + - openpyxl >=3.1.0 + - tzdata >=2022.7 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping - size: 11688777 - timestamp: 1755779667524 + size: 11635503 + timestamp: 1759266458775 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hadf4263_0.conda + sha256: 3613774ad27e48503a3a6a9d72017087ea70f1426f6e5541dbdb59a3b626eaaf + md5: 79f71230c069a287efe3a8614069ddf1 + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.4,<2.0a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - fribidi >=1.0.10,<2.0a0 + - harfbuzz >=11.0.1 + - libexpat >=2.7.0,<3.0a0 + - libfreetype >=2.13.3 + - libfreetype6 >=2.13.3 + - libgcc >=13 + - libglib >=2.84.2,<3.0a0 + - libpng >=1.6.49,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + license: LGPL-2.1-or-later + purls: [] + size: 455420 + timestamp: 1751292466873 - conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.56.4-h03d888a_0.conda sha256: dcda7e9bedc1c87f51ceef7632a5901e26081a1f74a89799a3e50dbdc801c0bd md5: 452d6d3b409edead3bd90fc6317cd6d4 @@ -3341,69 +7232,121 @@ packages: - pkg:pypi/pathspec?source=hash-mapping size: 41075 timestamp: 1733233471940 -- conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.1-pyhd8ed1ab_1.conda - sha256: ab52916f056b435757d46d4ce0a93fd73af47df9c11fd72b74cc4b7e1caca563 - md5: ee23fabfd0a8c6b8d6f3729b47b2859d +- conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda + sha256: 9678f4745e6b82b36fab9657a19665081862268cb079cf9acf878ab2c4fadee9 + md5: 8678577a52161cc4e1c93fcc18e8a646 depends: - numpy >=1.4.0 - - python >=3.9 + - python >=3.10 + - python license: BSD-2-Clause AND PSF-2.0 - license_family: BSD purls: - pkg:pypi/patsy?source=hash-mapping - size: 186594 - timestamp: 1733792482894 -- conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.45-h99c9b8b_0.conda - sha256: 165d6f76e7849615cfa5fe5f0209b90103102db17a7b4632f933fa9c0e8d8bfe - md5: f4c483274001678e129f5cbaf3a8d765 + size: 193450 + timestamp: 1760998269054 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.46-h1321c63_0.conda + sha256: 5c7380c8fd3ad5fc0f8039069a45586aa452cf165264bc5a437ad80397b32934 + md5: 7fa07cb0fb1b625a089ccc01218ee5b1 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1209177 + timestamp: 1756742976157 +- conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.46-h3402e2f_0.conda + sha256: 29c2ed44a8534d27faad96bdce16efe29c2788f556f4c5409d4ae8ae074681ec + md5: 889053e920d15353c2665fa6310d7a7a depends: - bzip2 >=1.0.8,<2.0a0 - libzlib >=1.3.1,<2.0a0 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD purls: [] - size: 1040584 - timestamp: 1745955875845 -- conda: https://conda.anaconda.org/conda-forge/win-64/pillow-11.3.0-py310h6d647b9_0.conda - sha256: d7fd5a0b9ca4cc9fe945cae018cf054aa3b6139cd9022290e8c35e163829ef72 - md5: 246b33a0eb812754b529065262aeb1c5 + size: 1034703 + timestamp: 1756743085974 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py310h6557065_3.conda + sha256: 7fe27fd1c5a3d85ea355a609d050e50469382223bbf5a07ca750e30b6aebdc25 + md5: e169733dc0c743687a852f1c6e989140 depends: - - lcms2 >=2.17,<3.0a0 - - libfreetype >=2.13.3 - - libfreetype6 >=2.13.3 - - libjpeg-turbo >=3.1.0,<4.0a0 + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 - libtiff >=4.7.0,<4.8.0a0 - - libwebp-base >=1.5.0,<2.0a0 + - libwebp-base >=1.6.0,<2.0a0 - libxcb >=1.17.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - openjpeg >=2.5.3,<3.0a0 - - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - lcms2 >=2.17,<3.0a0 - tk >=8.6.13,<8.7.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - openjpeg >=2.5.3,<3.0a0 + - libzlib >=1.3.1,<2.0a0 + license: HPND + purls: + - pkg:pypi/pillow?source=hash-mapping + size: 882171 + timestamp: 1758208668856 +- conda: https://conda.anaconda.org/conda-forge/win-64/pillow-11.3.0-py310hb3a2f59_3.conda + sha256: d4825cfe13dc10bb90553c663b86128c8e53bb8c3eda768f7d0d29c3ad878a92 + md5: a12291a9a7acce46716c518c31ebb298 + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - tk >=8.6.13,<8.7.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.3,<3.0a0 + - libxcb >=1.17.0,<2.0a0 + - lcms2 >=2.17,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - python_abi 3.10.* *_cp310 license: HPND purls: - pkg:pypi/pillow?source=hash-mapping - size: 42010130 - timestamp: 1751482283364 -- conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda - sha256: ec9ed3cef137679f3e3a68e286c6efd52144684e1be0b05004d9699882dadcdd - md5: dfce4b2af4bfe90cdcaf56ca0b28ddf5 + size: 786227 + timestamp: 1758208682963 +- conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.3-pyh8b19718_0.conda + sha256: b67692da1c0084516ac1c9ada4d55eaf3c5891b54980f30f3f444541c2706f1e + md5: c55515ca43c6444d2572e0f0d93cb6b9 depends: - - python >=3.9,<3.13.0a0 + - python >=3.10,<3.13.0a0 - setuptools - wheel license: MIT license_family: MIT purls: - - pkg:pypi/pip?source=hash-mapping - size: 1177168 - timestamp: 1753924973872 + - pkg:pypi/pip?source=compressed-mapping + size: 1177534 + timestamp: 1762776258783 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda + sha256: 43d37bc9ca3b257c5dd7bf76a8426addbdec381f6786ff441dc90b1a49143b6a + md5: c01af13bdc553d1a8fbfff6e8db075f0 + depends: + - libgcc >=14 + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + purls: [] + size: 450960 + timestamp: 1754665235234 - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_1.conda sha256: 246fce4706b3f8b247a7d6142ba8d732c95263d3c96e212b9d63d6a4ab4aff35 md5: 08c8fa3b419df480d985e304f7884d35 @@ -3419,47 +7362,157 @@ packages: purls: [] size: 542795 timestamp: 1754665193489 -- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.4.0-pyhcf101f3_0.conda - sha256: dfe0fa6e351d2b0cef95ac1a1533d4f960d3992f9e0f82aeb5ec3623a699896b - md5: cc9d9a3929503785403dbfad9f707145 +- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda + sha256: 7efd51b48d908de2d75cbb3c4a2e80dd9454e1c5bb8191b261af3136f7fa5888 + md5: 5c7a868f8241e64e1cf5fdf4962f23e2 depends: - python >=3.10 - python license: MIT + license_family: MIT purls: - - pkg:pypi/platformdirs?source=compressed-mapping - size: 23653 - timestamp: 1756227402815 -- conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.3.0-pyhd8ed1ab_0.conda - sha256: de59e60bdb5f42a6da18821e49545a0040c1f6940360c6177b5e3a350cc96d51 - md5: 5366b5b366cd3a2efa7e638792972ea1 + - pkg:pypi/platformdirs?source=hash-mapping + size: 23625 + timestamp: 1759953252315 +- conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.5.0-pyhd8ed1ab_0.conda + sha256: 13b06d2380fc46c299d2ae3465f90f156929b7f98597fc22b0e7ac0cfd40c20d + md5: 6d4c79b604d50c1140c32164f7eca72a depends: - narwhals >=1.15.1 - packaging - - python >=3.9 + - python >=3.10 constrains: - ipywidgets >=7.6 license: MIT license_family: MIT purls: - pkg:pypi/plotly?source=hash-mapping - size: 4921172 - timestamp: 1755067356284 -- conda: https://conda.anaconda.org/conda-forge/win-64/portmidi-2.0.6-hac47afa_0.conda - sha256: 9d4e1f70d2bad598346bbad4f8b477f2f46eed0b9c79ce062f958038b30bde23 - md5: bf53d64c3bbe81a1f02b7497d2763614 + size: 5179039 + timestamp: 1763430425844 +- conda: https://conda.anaconda.org/conda-forge/noarch/polars-1.35.2-pyh6a1acc5_0.conda + sha256: 21619fb41876fb3ad602de2f1a493891fd02bd82f0659d0ccb4d8ddd3271eca7 + md5: 24e8f78d79881b3c035f89f4b83c565c + depends: + - polars-runtime-32 ==1.35.2 + - python >=3.10 + - python + constrains: + - numpy >=1.16.0 + - pyarrow >=7.0.0 + - fastexcel >=0.9 + - openpyxl >=3.0.0 + - xlsx2csv >=0.8.0 + - connectorx >=0.3.2 + - deltalake >=1.0.0 + - pyiceberg >=0.7.1 + - altair >=5.4.0 + - great_tables >=0.8.0 + - polars-runtime-32 ==1.35.2 + - polars-runtime-64 ==1.35.2 + - polars-runtime-compat ==1.35.2 + license: MIT + license_family: MIT + purls: + - pkg:pypi/polars?source=hash-mapping + size: 513921 + timestamp: 1763738199817 +- conda: https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-32-1.35.2-py310hffdcd12_0.conda + noarch: python + sha256: a2294d8079173544b519e1564ce4897dc176b92dbba9508c75c42e8cba6fe680 + md5: 2b90c3aaf73a5b6028b068cf3c76e0b7 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - _python_abi3_support 1.* + - cpython >=3.10 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + purls: + - pkg:pypi/polars-runtime-32?source=hash-mapping + size: 33271651 + timestamp: 1763738199812 +- conda: https://conda.anaconda.org/conda-forge/win-64/polars-runtime-32-1.35.2-py310hca7251b_0.conda + noarch: python + sha256: 03fcd84f828086be2cf658fb6250dfa665da241900f61326cae4c5d8c49642b7 + md5: 56f24763ee87604f694db3e0c74ae38c depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - - vc14_runtime >=14.42.34438 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - _python_abi3_support 1.* + - cpython >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/polars-runtime-32?source=compressed-mapping + size: 36375197 + timestamp: 1763738028556 +- conda: https://conda.anaconda.org/conda-forge/linux-64/portaudio-19.6.0-h7c63dc7_9.conda + sha256: c09ae032d0303abfea34c0957834538b48133b0431283852741ed3e0f66fdb36 + md5: 893f2c33af6b03cfd04820a8c31f5798 + depends: + - alsa-lib >=1.2.10,<1.3.0.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + purls: [] + size: 115512 + timestamp: 1693868383 +- conda: https://conda.anaconda.org/conda-forge/linux-64/portmidi-2.0.7-h3cb78e3_0.conda + sha256: 7139889de1da0e5990c9baface279c62c6be71bfb650897ecc92b9fa84aaa482 + md5: 378b689c31af1260dbf6e5e5eb02166c + depends: + - __glibc >=2.17,<3.0.a0 + - alsa-lib >=1.2.14,<1.3.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 44047 + timestamp: 1761471639423 +- conda: https://conda.anaconda.org/conda-forge/win-64/portmidi-2.0.7-hac47afa_0.conda + sha256: 526075a85a406b1e1a204a8fa9330bf880cc463e517e8f23d960419370a80f4a + md5: 3fbe60c5c1c6414d4d36a9af5ba91588 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + purls: [] + size: 45866 + timestamp: 1761471797147 +- conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.0-hb72c0af_0.conda + sha256: f1c5e1cc0de088fd3458009be68095f561fa74b5ca6293dcca266f1854d859df + md5: 438e75abf4d8c9c1d9e483b6c3f36282 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.14.1,<9.0a0 + - libgcc >=14 + - libsqlite >=3.50.4,<4.0a0 + - libstdcxx >=14 + - libtiff >=4.7.0,<4.8.0a0 + - sqlite + constrains: + - proj4 ==999999999999 license: MIT license_family: MIT purls: [] - size: 45615 - timestamp: 1750372590470 -- conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.6.2-h7990399_2.conda - sha256: e798e9bd658f6c00cfac0d8573c7fe97d9ebad5966c96c23e0702f44e51905bb - md5: 6e0e8fcc3eb2c1418d663005bf040d8d + size: 3259440 + timestamp: 1757929968903 +- conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.7.0-h9080b7b_0.conda + sha256: 5f1b172b160bf2f1aea02752b52707f503a2522bcd560dd08205795082d96571 + md5: 0e7974e38102314107afaec3c37183a0 depends: - libcurl >=8.14.1,<9.0a0 - libsqlite >=3.50.4,<4.0a0 @@ -3473,8 +7526,22 @@ packages: license: MIT license_family: MIT purls: [] - size: 2788230 - timestamp: 1754928361098 + size: 2769241 + timestamp: 1757930535432 +- conda: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.3.1-py310h89163eb_0.conda + sha256: 3dbf885bb1eb0e7a5eb3779165517abdb98d53871b36690041f6a366cc501738 + md5: e768486f2be3f50126bf9a54331221d1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/propcache?source=hash-mapping + size: 53576 + timestamp: 1744525075233 - conda: https://conda.anaconda.org/conda-forge/win-64/propcache-0.3.1-py310h38315fa_0.conda sha256: 75f9e936e5a4d47d9cbecc5eacdcb30ef120e05ff0043df709da268bcf20b7be md5: 86c5d36fa088bc45157532ba94608e39 @@ -3490,21 +7557,87 @@ packages: - pkg:pypi/propcache?source=hash-mapping size: 50113 timestamp: 1744525434712 -- conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.0.0-py310h29418f3_1.conda - sha256: ae31f38509f1b92a4f27cfdd3cabea269172cb2912e85581671e2b27df15e561 - md5: 02aed3c30affdc36098278220f0ab5fd +- conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.31.1-py310ha36e12e_2.conda + sha256: c9546dc61a66fabab16a7730b80cca083a22593fc889b496b56940fa05ef8be2 + md5: e89aa169330695f7694c94d9a95d20bc + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + constrains: + - libprotobuf 6.31.1 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/protobuf?source=hash-mapping + size: 405059 + timestamp: 1760393380060 +- conda: https://conda.anaconda.org/conda-forge/win-64/protobuf-6.31.1-py310h81b7714_2.conda + sha256: f5c83f900ae6740b2773b89cf2befebea40363cbe464397cfd33098f7bca9fff + md5: ace895b7d8a5cc36111033e1e3129d14 depends: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - ucrt >=10.0.20348.0 + - vc >=14.2 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + constrains: + - libprotobuf 6.31.1 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/protobuf?source=hash-mapping + size: 408632 + timestamp: 1760394135407 +- conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.3-py310h139afa4_0.conda + sha256: aca45f6bfe5ee0e0831f3c6840dcd38ebc99c30be85e20d02718ab4e15698bfb + md5: 2cb444ad9954c0a0e59a65bbac84305b + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python_abi 3.10.* *_cp310 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/psutil?source=hash-mapping + size: 375067 + timestamp: 1762092922662 +- conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.1.3-py310h1637853_0.conda + sha256: 5634acc515e32ee0a3d7d8783acb06668958c26d5d6309a9ef5e0b66fca21fa7 + md5: 24c099af19cf88a4cf87d39c73a7197b + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.10.* *_cp310 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/psutil?source=hash-mapping - size: 369743 - timestamp: 1755851688865 + size: 393244 + timestamp: 1762092906095 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 + md5: b3c17d95b5a10c6e64a21fa17573e70e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 8252 + timestamp: 1726802366959 - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda sha256: 7e446bafb4d692792310ed022fe284e848c6a868c861655a92435af7368bae7b md5: 3c8f2573569bb816483e5cf57efbbe29 @@ -3517,6 +7650,18 @@ packages: purls: [] size: 9389 timestamp: 1726802555076 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pugixml-1.15-h3f63f65_0.conda + sha256: 23c98a5000356e173568dc5c5770b53393879f946f3ace716bbdefac2a8b23d2 + md5: b11a4c6bf6f6f44e5e143f759ffa2087 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + purls: [] + size: 118488 + timestamp: 1736601364156 - conda: https://conda.anaconda.org/conda-forge/win-64/pugixml-1.15-h372dad0_0.conda sha256: 97b34ed73b6f559fcf5e706d4c8435923ba95cfed478d3fd50b475f94f60dc6e md5: cadea4c6edb512e979edbf793bf979ac @@ -3529,6 +7674,25 @@ packages: purls: [] size: 113967 timestamp: 1736601565527 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-h9a6aba3_3.conda + sha256: 0a0858c59805d627d02bdceee965dd84fde0aceab03a2f984325eec08d822096 + md5: b8ea447fdf62e3597cb8d2fae4eb1a90 + depends: + - __glibc >=2.17,<3.0.a0 + - dbus >=1.16.2,<2.0a0 + - libgcc >=14 + - libglib >=2.86.1,<3.0a0 + - libiconv >=1.18,<2.0a0 + - libsndfile >=1.2.2,<1.3.0a0 + - libsystemd0 >=257.10 + - libxcb >=1.17.0,<2.0a0 + constrains: + - pulseaudio 17.0 *_3 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + size: 750785 + timestamp: 1763148198088 - conda: https://conda.anaconda.org/conda-forge/noarch/py-cpuinfo-9.0.0-pyhd8ed1ab_1.conda sha256: 6d8f03c13d085a569fde931892cded813474acbef2e03381a1a87f420c7da035 md5: 46830ee16925d5ed250850503b5dc3a8 @@ -3540,11 +7704,25 @@ packages: - pkg:pypi/py-cpuinfo?source=hash-mapping size: 25766 timestamp: 1733236452235 -- conda: https://conda.anaconda.org/conda-forge/win-64/py-opencv-4.12.0-qt6_py310h9217539_603.conda - sha256: 45de467524d521ec933d01b6189ee9e9958cc1f799912e899f89bde3d3daa614 - md5: 032b3fb0ba9a0d2ada7c8b04d3675d4f +- conda: https://conda.anaconda.org/conda-forge/linux-64/py-opencv-4.12.0-qt6_py310h89973df_607.conda + sha256: 2f51ecd5841d5d8569dc3e1b4e3b71cc51670b61e44be62e0e7d70c110d1b768 + md5: fe068dc5964bdfd2686bf9536b556b90 + depends: + - libopencv 4.12.0 qt6_py310hdb0ca46_607 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - numpy >=1.21,<3 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 1154465 + timestamp: 1760195289902 +- conda: https://conda.anaconda.org/conda-forge/win-64/py-opencv-4.12.0-qt6_py310h9217539_607.conda + sha256: 71e037a2a3bb7b25a9568cce7bb6b4a43f534fbdc76c53365a4cd508c5b68f20 + md5: ab13a640810e04cddd9cf044964aab9d depends: - - libopencv 4.12.0 qt6_py310h5bb264c_603 + - libopencv 4.12.0 qt6_py310h3c58159_607 - libprotobuf >=6.31.1,<6.31.2.0a0 - numpy >=1.21,<3 - python >=3.10,<3.11.0a0 @@ -3552,8 +7730,27 @@ packages: license: Apache-2.0 license_family: Apache purls: [] - size: 1154223 - timestamp: 1755994820544 + size: 1155352 + timestamp: 1760196439069 +- conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-4-hd8ed1ab_3.tar.bz2 + sha256: d4fb485b79b11042a16dc6abfb0c44c4f557707c2653ac47c81e5d32b24a3bb0 + md5: 878f923dd6acc8aeb47a75da6c4098be + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 9906 + timestamp: 1610372835205 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyclean-3.4.0-pyhd8ed1ab_0.conda + sha256: 2cb825eafddec1a4a8436f984f281d613217b1c538cfb07152c05704d404ab1f + md5: a747b2d57c2d7da2fb287b905e5912b0 + depends: + - python >=3.9 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/pyclean?source=hash-mapping + size: 31049 + timestamp: 1762462567783 - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 md5: 12c566707c80111f9799308d9e265aef @@ -3566,6 +7763,31 @@ packages: - pkg:pypi/pycparser?source=hash-mapping size: 110100 timestamp: 1733195786147 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pygame-2.6.1-py310hae03094_0.conda + sha256: f6cf04a03311d4eefde641d640aa2d7d6247e923277dd7f003f58011d2ba67f6 + md5: 2b6236c72ba4ef405c09442fe7981a2b + depends: + - __glibc >=2.17,<3.0.a0 + - fontconfig >=2.14.2,<3.0a0 + - fonts-conda-ecosystem + - freetype >=2.12.1,<3.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - portmidi >=2.0.4,<3.0a0 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - sdl2 >=2.30.7,<3.0a0 + - sdl2_image >=2.8.2,<3.0a0 + - sdl2_mixer >=2.6.3,<3.0a0 + - sdl2_ttf >=2.22.0,<3.0a0 + license: LGPL-2.1-only + purls: + - pkg:pypi/pygame?source=hash-mapping + size: 2733919 + timestamp: 1727636699041 - conda: https://conda.anaconda.org/conda-forge/win-64/pygame-2.6.1-py310hea55160_0.conda sha256: 9f43830626745c8f1c3ce6684c749884a1c41b4056d2eba598945e6a25c48bd3 md5: 4181371878fa6a836f591f8778b50ca4 @@ -3602,52 +7824,80 @@ packages: - pkg:pypi/pygments?source=hash-mapping size: 889287 timestamp: 1750615908735 -- conda: https://conda.anaconda.org/conda-forge/noarch/pymdown-extensions-10.16.1-pyhd8ed1ab_0.conda - sha256: 8f575f123694e5acd2829440da55828f2cea60b0af5d8fa5406d83251ba80f61 - md5: 26e013bc453e643991cfa9b76911fb79 +- conda: https://conda.anaconda.org/conda-forge/noarch/pymdown-extensions-10.17.2-pyhd8ed1ab_0.conda + sha256: d4e73d4cdf8a25e50c4d47efee80fadcb84b3ec39ef437a5c0b2ddd31e945bca + md5: 0dcca5a6fe3dbd516fa806e378328f58 depends: - markdown >=3.6 - - python >=3.9 + - python >=3.10 - pyyaml license: MIT license_family: MIT purls: - pkg:pypi/pymdown-extensions?source=hash-mapping - size: 170121 - timestamp: 1753743741894 -- conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhe01879c_2.conda - sha256: afe32182b1090911b64ac0f29eb47e03a015d142833d8a917defd65d91c99b74 - md5: aa0028616c0750c773698fdc254b2b8d + size: 169650 + timestamp: 1764182363126 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda + sha256: 6814b61b94e95ffc45ec539a6424d8447895fef75b0fec7e1be31f5beee883fb + md5: 6c8979be6d7a17692793114fa26916e8 depends: - - python >=3.9 + - python >=3.10 - python license: MIT license_family: MIT purls: - - pkg:pypi/pyparsing?source=compressed-mapping - size: 102292 - timestamp: 1753873557076 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.1-py310h2d19612_0.conda - sha256: 888e430131bf704554850127d17926946e1ec67c531b9ef66ae4dced2d4ea775 - md5: 01b830c0fd6ca7ab03c85a008a6f4a2d - depends: - - libclang13 >=20.1.6 - - libxml2 >=2.13.8,<2.14.0a0 - - libxslt >=1.1.39,<2.0a0 + - pkg:pypi/pyparsing?source=hash-mapping + size: 104044 + timestamp: 1758436411254 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.9.3-py310h2007e60_1.conda + sha256: ddf4e8503a6f014fd2d37be6edbc0923ebe8f3b36e34279d37535121cb284274 + md5: 81c1ead40d87777cab2747cb6714e771 + depends: + - __glibc >=2.17,<3.0.a0 + - libclang13 >=21.1.2 + - libegl >=1.7.0,<2.0a0 + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libopengl >=1.7.0,<2.0a0 + - libstdcxx >=14 + - libvulkan-loader >=1.4.313.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libxslt >=1.1.43,<2.0a0 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - qt6-main 6.9.3.* + - qt6-main >=6.9.3,<6.10.0a0 + license: LGPL-3.0-only + license_family: LGPL + purls: + - pkg:pypi/pyside6?source=hash-mapping + - pkg:pypi/shiboken6?source=hash-mapping + size: 10115840 + timestamp: 1759403081665 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.3-py310h96c60bd_1.conda + sha256: d623634561e7af70fe7bd9cb3113bff4304061e2b0fb43d7f681a0d400fa85d7 + md5: 12fc2a59f16d715cc77b8c50933207d1 + depends: + - libclang13 >=21.1.2 + - libvulkan-loader >=1.4.313.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libxslt >=1.1.43,<2.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - - qt6-main 6.9.1.* - - qt6-main >=6.9.1,<6.10.0a0 + - qt6-main 6.9.3.* + - qt6-main >=6.9.3,<6.10.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - - vc14_runtime >=14.42.34438 + - vc14_runtime >=14.44.35208 license: LGPL-3.0-only license_family: LGPL purls: - pkg:pypi/pyside6?source=hash-mapping - pkg:pypi/shiboken6?source=hash-mapping - size: 8869574 - timestamp: 1749047999678 + size: 8860583 + timestamp: 1759403412457 - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda sha256: d016e04b0e12063fbee4a2d5fbb9b39a8d191b5a0042f0b8459188aedeabb0ca md5: e2fd202833c4a981ce8a65974fe4abd1 @@ -3661,28 +7911,69 @@ packages: - pkg:pypi/pysocks?source=hash-mapping size: 21784 timestamp: 1733217448189 -- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.10.18-h8c5b53a_0_cpython.conda - sha256: 548f9e542e72925d595c66191ffd17056f7c0029b7181e2d99dbef47e4f3f646 - md5: f1775dab55c8a073ebd024bfb2f689c1 +- conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 + md5: 461219d1a5bd61342293efa2c0c90eac + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pysocks?source=hash-mapping + size: 21085 + timestamp: 1733217331982 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.10.19-h3c07f61_2_cpython.conda + build_number: 2 + sha256: 6e3b6b69b3cacfc7610155d58407a003820eaacd50fbe039abff52b5e70b1e9b + md5: 27ac896a8b4970f8977503a9e70dc745 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.7.1,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=14 + - liblzma >=5.8.1,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.50.4,<4.0a0 + - libuuid >=2.41.2,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.5.4,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.10.* *_cp310 + license: Python-2.0 + purls: [] + size: 25311690 + timestamp: 1761173015969 +- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.10.19-hc20f281_2_cpython.conda + build_number: 2 + sha256: 58c3066571c9c8ba62254dfa1cee696d053f9f78cd3a92c8032af58232610c32 + md5: cd78c55405743e88fda2464be3c902b3 depends: - bzip2 >=1.0.8,<2.0a0 - - libexpat >=2.7.0,<3.0a0 + - libexpat >=2.7.1,<3.0a0 - libffi >=3.4,<4.0a0 - liblzma >=5.8.1,<6.0a0 - - libsqlite >=3.50.0,<4.0a0 + - libsqlite >=3.50.4,<4.0a0 - libzlib >=1.3.1,<2.0a0 - - openssl >=3.5.0,<4.0a0 + - openssl >=3.5.4,<4.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 constrains: - python_abi 3.10.* *_cp310 license: Python-2.0 purls: [] - size: 15832933 - timestamp: 1749048670944 + size: 16106778 + timestamp: 1761172101787 - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda sha256: d6a17ece93bbd5139e02d2bd7dbfa80bee1a4261dced63f65f679121686bf664 md5: 5b8d21249ff20967101ffa321cab24e8 @@ -3696,6 +7987,16 @@ packages: - pkg:pypi/python-dateutil?source=hash-mapping size: 233310 timestamp: 1751104122689 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.10.19-hd8ed1ab_2.conda + sha256: 460ea488d7b545eb434cb58a6022df14bd0ba0a9cbe2d1b5816ba9fc268615c3 + md5: 6f3b7a672f47e87de27a9941b5876f60 + depends: + - cpython 3.10.19.* + - python_abi * *_cp310 + license: Python-2.0 + purls: [] + size: 50183 + timestamp: 1761172142297 - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda sha256: e8392a8044d56ad017c08fec2b0eb10ae3d1235ac967d0aab8bd7b41c4a5eaf0 md5: 88476ae6ebd24f39261e0854ac244f33 @@ -3729,22 +8030,37 @@ packages: - pkg:pypi/pytz?source=hash-mapping size: 189015 timestamp: 1742920947249 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.2-py310h38315fa_2.conda - sha256: 49dd492bdf2c479118ca9d61a59ce259594853d367a1a0548926f41a6e734724 - md5: 9986c3731bb820db0830dd0825c26cf9 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py310h3406613_0.conda + sha256: 9b5c6ff9111ac035f18d5e625bcaa6c076e2e64a6f3c8e3f83f5fe2b03bda78d + md5: bc058b3b89fcb525bb4977832aa52014 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping + size: 180966 + timestamp: 1758892005321 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py310hdb0e946_0.conda + sha256: a2f80973dae258443b33a07266de8b8a7c9bf91cda41d5a3a907ce9553d79b0b + md5: c6c1bf08ce99a6f5dc7fdb155b088b26 depends: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping - size: 157941 - timestamp: 1737455030235 + size: 158156 + timestamp: 1758891961665 - conda: https://conda.anaconda.org/conda-forge/noarch/pyyaml-env-tag-1.1-pyhd8ed1ab_0.conda sha256: 69ab63bd45587406ae911811fc4d4c1bf972d643fa57a009de7c01ac978c4edd md5: e8e53c4150a1bba3b160eacf9d53a51b @@ -3757,6 +8073,17 @@ packages: - pkg:pypi/pyyaml-env-tag?source=hash-mapping size: 11137 timestamp: 1747237061448 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda + sha256: 776363493bad83308ba30bcb88c2552632581b143e8ee25b1982c8c743e73abc + md5: 353823361b1d27eb3960efb076dfcaf6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: LicenseRef-Qhull + purls: [] + size: 552937 + timestamp: 1720813982144 - conda: https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda sha256: 887d53486a37bd870da62b8fa2ebe3993f912ad04bd755e7ed7c47ced97cbaa8 md5: 854fbdff64b572b5c0b470f334d34c11 @@ -3768,35 +8095,113 @@ packages: purls: [] size: 1377020 timestamp: 1720814433486 -- conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.1-h02ddd7d_2.conda - sha256: 0093da5504b42a3b26ec10cdedc07d91e83225775590f596407b9de769ca4a5f - md5: 3cbddb0b12c72aa3b974a4d12af51f29 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.3-h5c1c036_1.conda + sha256: 51537408ce1493d267b375b33ec02a060d77c4e00c7bef5e2e1c6724e08a23e3 + md5: 762af6d08fdfa7a45346b1466740bacd depends: + - __glibc >=2.17,<3.0.a0 + - alsa-lib >=1.2.14,<1.3.0a0 + - dbus >=1.16.2,<2.0a0 - double-conversion >=3.3.1,<3.4.0a0 - - harfbuzz >=11.0.1 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - harfbuzz >=12.1.0 - icu >=75.1,<76.0a0 - krb5 >=1.21.3,<1.22.0a0 - - libclang13 >=20.1.8 - - libglib >=2.84.2,<3.0a0 + - libclang-cpp21.1 >=21.1.4,<21.2.0a0 + - libclang13 >=21.1.4 + - libcups >=2.3.3,<2.4.0a0 + - libdrm >=2.4.125,<2.5.0a0 + - libegl >=1.7.0,<2.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libglib >=2.86.0,<3.0a0 - libjpeg-turbo >=3.1.0,<4.0a0 + - libllvm21 >=21.1.4,<21.2.0a0 - libpng >=1.6.50,<1.7.0a0 - - libsqlite >=3.50.3,<4.0a0 - - libtiff >=4.7.0,<4.8.0a0 + - libpq >=18.0,<19.0a0 + - libsqlite >=3.50.4,<4.0a0 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + - libvulkan-loader >=1.4.328.1,<2.0a0 - libwebp-base >=1.6.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libxkbcommon >=1.12.2,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 - libzlib >=1.3.1,<2.0a0 - - openssl >=3.5.1,<4.0a0 - - pcre2 >=10.45,<10.46.0a0 + - openssl >=3.5.4,<4.0a0 + - pcre2 >=10.46,<10.47.0a0 + - wayland >=1.24.0,<2.0a0 + - xcb-util >=0.4.1,<0.5.0a0 + - xcb-util-cursor >=0.1.5,<0.2.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - xcb-util-keysyms >=0.4.1,<0.5.0a0 + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + - xcb-util-wm >=0.4.2,<0.5.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.6,<2.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxcomposite >=0.4.6,<1.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - xorg-libxdamage >=1.1.6,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrandr >=1.5.4,<2.0a0 + - xorg-libxtst >=1.2.5,<2.0a0 + - xorg-libxxf86vm >=1.1.6,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - qt 6.9.3 + license: LGPL-3.0-only + license_family: LGPL + purls: [] + size: 54785664 + timestamp: 1761308850008 +- conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.3-ha0de62e_1.conda + sha256: 257b999442d4e14e1e061890e7bd0620511f57324df3ad27bb3cf78b2a6cdcb3 + md5: ca2bfad3a24794a0f7cf413b03906ade + depends: + - double-conversion >=3.3.1,<3.4.0a0 + - harfbuzz >=12.1.0 + - icu >=75.1,<76.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libclang13 >=21.1.4 + - libglib >=2.86.0,<3.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - libpng >=1.6.50,<1.7.0a0 + - libsqlite >=3.50.4,<4.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libvulkan-loader >=1.4.328.1,<2.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - pcre2 >=10.46,<10.47.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - zstd >=1.5.7,<1.6.0a0 constrains: - - qt 6.9.1 + - qt 6.9.3 license: LGPL-3.0-only license_family: LGPL purls: [] - size: 94478713 - timestamp: 1753285797220 + size: 95659243 + timestamp: 1761312853504 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.7.1-h8fae777_3.conda + sha256: 6e5e704c1c21f820d760e56082b276deaf2b53cf9b751772761c3088a365f6f4 + md5: 2c42649888aac645608191ffdc80d13a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - __glibc >=2.17 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 5176669 + timestamp: 1746622023242 - conda: https://conda.anaconda.org/conda-forge/win-64/rav1e-0.7.1-ha073cba_3.conda sha256: d19a58b882a0387c7c8efbfce4e67a0df4b19d8da6cf6cec3011b6079e5bc743 md5: 3bd3626822633688691ed41d661c2b2e @@ -3809,6 +8214,37 @@ packages: purls: [] size: 4122383 timestamp: 1746622805379 +- conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_0.conda + sha256: 2f225ddf4a274743045aded48053af65c31721e797a45beed6774fdc783febfb + md5: 0227d04521bc3d28c7995c7e1f99a721 + depends: + - libre2-11 2025.11.05 h7b12aa8_0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 27316 + timestamp: 1762397780316 +- conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-ha104f34_0.conda + sha256: 9d1bb3d15cdd3257baee5fc063221514482f91154cd1457af126e1ec460bbeac + md5: 50746f61f199c4c00d42e33f5d6cfd0b + depends: + - libre2-11 2025.11.05 h0eb2380_0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 216623 + timestamp: 1762397986736 +- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + sha256: 2d6d0c026902561ed77cd646b5021aef2d4db22e57a5b0178dfc669231e06d2c + md5: 283b96675859b20a825f8fa30f311446 + depends: + - libgcc >=13 + - ncurses >=6.5,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 282480 + timestamp: 1740379431762 - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda sha256: 8dc54e94721e9ab545d7234aa5192b74102263d3e704e6d0c8aa7008f2da2a7b md5: db0c6b99149880c8ba515cf4abe93ee4 @@ -3823,7 +8259,7 @@ packages: license: Apache-2.0 license_family: APACHE purls: - - pkg:pypi/requests?source=compressed-mapping + - pkg:pypi/requests?source=hash-mapping size: 59263 timestamp: 1755614348400 - conda: https://conda.anaconda.org/conda-forge/noarch/retrying-1.4.2-pyhe01879c_0.conda @@ -3838,9 +8274,30 @@ packages: - pkg:pypi/retrying?source=hash-mapping size: 20907 timestamp: 1754219562310 -- conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.7.1-py310h21054b0_0.conda - sha256: 7b8b6c0aad48460b0d65478f39eec2a1651650058fbd7628401d1feac17eae5f - md5: ff213e9fcac983ba915ab5d9cb6001e9 +- conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.7.2-py310h228f341_0.conda + sha256: 8af2a49b75b9697653a0bf55717c8153732c4fc53f58d4aa0ed692ae348c49f6 + md5: 0f3e3324506bd3e67934eda9895f37a7 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - joblib >=1.2.0 + - libgcc >=14 + - libstdcxx >=14 + - numpy >=1.21,<3 + - numpy >=1.22.0 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - scipy >=1.8.0 + - threadpoolctl >=3.1.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scikit-learn?source=hash-mapping + size: 8415134 + timestamp: 1757406407327 +- conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.7.2-py310h21054b0_0.conda + sha256: 8515607e6b0e97edd93120368a9e788f3149fdc0855252a9d112e32ad522ba4a + md5: 2b93662be724f88f5196549133adbfe1 depends: - joblib >=1.2.0 - numpy >=1.21,<3 @@ -3856,8 +8313,31 @@ packages: license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping - size: 7643944 - timestamp: 1752826558012 + size: 7675668 + timestamp: 1757433583495 +- conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py310h1d65ade_0.conda + sha256: 4cb98641f870666d365594013701d5691205a0fe81ac3ba7778a23b1cc2caa8e + md5: 8c29cd33b64b2eb78597fa28b5595c8d + depends: + - __glibc >=2.17,<3.0.a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc >=13 + - libgfortran + - libgfortran5 >=13.3.0 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx >=13 + - numpy <2.5 + - numpy >=1.19,<3 + - numpy >=1.23.5 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping + size: 16417101 + timestamp: 1739791865060 - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.15.2-py310h15c175c_0.conda sha256: f19350c2061b1cdc3151a33c3dd4f71a1a481f9b10ac186674f957814bc839bc md5: 81798168111d1021e3d815217c444418 @@ -3879,21 +8359,52 @@ packages: - pkg:pypi/scipy?source=hash-mapping size: 14352068 timestamp: 1739793156239 -- conda: https://conda.anaconda.org/conda-forge/win-64/sdl2-2.32.54-he0c23c2_0.conda - sha256: 477781545f317cd9f0a35cc39e22976ee374f9c98b5cbb083812f6d33cf47c08 - md5: b1a715daa818f0ffcd23bb02b7fcf861 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2-2.32.56-h54a6638_0.conda + sha256: 987ad072939fdd51c92ea8d3544b286bb240aefda329f9b03a51d9b7e777f9de + md5: cdd138897d94dc07d99afe7113a07bec + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libgl >=1.7.0,<2.0a0 + - sdl3 >=3.2.22,<4.0a0 + - libegl >=1.7.0,<2.0a0 + license: Zlib + purls: [] + size: 589145 + timestamp: 1757842881 +- conda: https://conda.anaconda.org/conda-forge/win-64/sdl2-2.32.56-h5112557_0.conda + sha256: d17da21386bdbf32bce5daba5142916feb95eed63ef92b285808c765705bbfd2 + md5: 4cffbfebb6614a1bff3fc666527c25c7 depends: - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - sdl3 >=3.2.10,<4.0a0 + - sdl3 >=3.2.22,<4.0a0 + license: Zlib + purls: [] + size: 572101 + timestamp: 1757842925694 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2_image-2.8.2-h06ee604_1.conda + sha256: f18184e016e2e57306d1540dea584d38f4617d7ddb6aad4af6b5f21c52fa39ea + md5: 65e113270b460dcdfc4dc0a80bb3d11c + depends: + - libavif16 >=1.0.4,<2.0a0 + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libpng >=1.6.43,<1.7.0a0 + - libstdcxx-ng >=12 + - libtiff >=4.6.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.2.13,<2.0.0a0 + - sdl2 >=2.30.2,<3.0a0 license: Zlib purls: [] - size: 572859 - timestamp: 1745799945033 + size: 152110 + timestamp: 1716857107234 - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2_image-2.8.2-h5ca362a_1.conda sha256: 228ff6a2984b1f193bfe31f8ea527b96cfb553798b45ee3081f68be3c3b9175a md5: efe612d861e6df150608090eada73feb @@ -3912,6 +8423,24 @@ packages: purls: [] size: 118221 timestamp: 1716857408583 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2_mixer-2.6.3-h8830914_1.conda + sha256: c3e99e222b091f26cfd1d6be22c5a2973df9e7caa020262f9d9523f340344a95 + md5: 1a2b60be4d860a0c419a87176c85c3ad + depends: + - fluidsynth >=2.3.4,<2.4.0a0 + - libflac >=1.4.3,<1.5.0a0 + - libgcc-ng >=12 + - libmad >=0.15.1b,<0.16.0a0 + - libogg >=1.3.4,<1.4.0a0 + - libstdcxx-ng >=12 + - libvorbis >=1.3.7,<1.4.0a0 + - mpg123 >=1.32.1,<1.33.0a0 + - opusfile >=0.12,<0.13.0a0 + - sdl2 >=2.28.3,<3.0a0 + license: Zlib + purls: [] + size: 202966 + timestamp: 1695761744535 - conda: https://conda.anaconda.org/conda-forge/win-64/sdl2_mixer-2.6.3-he8f0768_1.conda sha256: dca3934ea1d72f8ef6998bb6d459e1fb232696b7f755f51c1bd54993b1d3d789 md5: 2f567617cd98f43fb8592d0052607f86 @@ -3929,23 +8458,66 @@ packages: purls: [] size: 170772 timestamp: 1695762073626 -- conda: https://conda.anaconda.org/conda-forge/win-64/sdl2_ttf-2.24.0-hd2a6e60_1.conda - sha256: 92fe10b4f930f2e6a7e6a1f2b101d64f1cbbb31d5d66e726478367df2617d9e2 - md5: b555346643f7a2f0cbc8d06fe59e1f36 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2_ttf-2.24.0-h287479f_0.conda + sha256: 431d19b666db6e7a4f09c37c43c83f115176a006b2ac321853ca26bee888c519 + md5: bccd5b74eb55a523dfcc66b857555714 depends: - - freetype >=2.13.3,<3.0a0 - - harfbuzz >=11.0.0,<12.0a0 - - sdl2 >=2.32.50,<3.0a0 + - __glibc >=2.17,<3.0.a0 + - freetype >=2.12.1,<3.0a0 + - harfbuzz >=10.1.0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - sdl2 >=2.30.10,<3.0a0 + license: Zlib + purls: [] + size: 61954 + timestamp: 1736117956977 +- conda: https://conda.anaconda.org/conda-forge/win-64/sdl2_ttf-2.24.0-hb42d5a6_0.conda + sha256: a7c2f068ad988bb67032e92aca6cbb5c53ea79552f0e959d3d54f57d5a91f910 + md5: 85f8a77cf314b66eb6d470bd593712cc + depends: + - freetype >=2.12.1,<3.0a0 + - harfbuzz >=10.1.0 + - sdl2 >=2.30.10,<3.0a0 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: Zlib purls: [] - size: 49071 - timestamp: 1743198907063 -- conda: https://conda.anaconda.org/conda-forge/win-64/sdl3-3.2.20-h5112557_0.conda - sha256: 93f1f8da77a42a3c916a098235f11760e50f1121b021f589b20b765168539563 - md5: 2d1eeda10a8704d14f47b232166f5bc3 + size: 48832 + timestamp: 1736118369014 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sdl3-3.2.26-h68140b3_0.conda + sha256: 31bfb3db00feb74dab5c3420b6b7529cd9a044fda381c422adc817df09ae17b1 + md5: 8d9c193907f1c9defb46322f06990105 + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libxscrnsaver >=1.2.4,<2.0a0 + - libvulkan-loader >=1.4.328.1,<2.0a0 + - liburing >=2.12,<2.13.0a0 + - libusb >=1.0.29,<2.0a0 + - libegl >=1.7.0,<2.0a0 + - libunwind >=1.8.3,<1.9.0a0 + - libgl >=1.7.0,<2.0a0 + - dbus >=1.16.2,<2.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + - libudev1 >=257.9 + - pulseaudio-client >=17.0,<17.1.0a0 + - libxkbcommon >=1.12.3,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + - libdrm >=2.4.125,<2.5.0a0 + - wayland >=1.24.0,<2.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: Zlib + purls: [] + size: 1936312 + timestamp: 1761847993797 +- conda: https://conda.anaconda.org/conda-forge/win-64/sdl3-3.2.26-h5112557_0.conda + sha256: ae620eb7da71a5961f3ba104e0d9a76b975ca2ff27f3740ce8053c331b59d764 + md5: ff6a20fd8441ec90a1c94ae077258135 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -3954,10 +8526,11 @@ packages: - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - libusb >=1.0.29,<2.0a0 + - libvulkan-loader >=1.4.328.1,<2.0a0 license: Zlib purls: [] - size: 1521861 - timestamp: 1754349758510 + size: 1520562 + timestamp: 1761848030770 - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda noarch: python sha256: ea29a69b14dd6be5cdeeaa551bf50d78cafeaf0351e271e358f9b820fcab4cb0 @@ -3998,23 +8571,67 @@ packages: - pkg:pypi/setuptools?source=hash-mapping size: 748788 timestamp: 1748804951958 -- conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.1-py310hb052cae_0.conda - sha256: 83d1ba7082a32db8e0def6212008cb8f2791cb3d088574d39cead5c1ae694dc7 - md5: 3f1f847a131559dcdc1dfb442843fc4a +- conda: https://conda.anaconda.org/conda-forge/linux-64/shaderc-2025.4-h3e344bc_0.conda + sha256: 638cf498db667ea449be531aba2f1c3b7784bd57dd44dacdfcd033f0e3deec8d + md5: d3b1d75357bce20e29a5ba4072603889 + depends: + - __glibc >=2.17,<3.0.a0 + - glslang >=16,<17.0a0 + - libgcc >=14 + - libstdcxx >=14 + - spirv-tools >=2025,<2026.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 112823 + timestamp: 1758916816431 +- conda: https://conda.anaconda.org/conda-forge/win-64/shaderc-2025.4-haa9a63f_0.conda + sha256: 20d5a983dcf43af980fbdaddf4de9c2509a9ccae9a0b41bbdbdd96cfc937e750 + md5: a28a3e63a3534b42fa4f4b4b3f0d4e3a depends: - - geos >=3.13.1,<3.13.2.0a0 - - numpy >=1.19,<3 + - glslang >=16,<17.0a0 + - spirv-tools >=2025,<2026.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 1473258 + timestamp: 1758917199117 +- conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py310hc8bbb35_2.conda + sha256: 292acdbb958db8141f1abc7b7a77970a930a9f73abeb0c6be399c50505c715ab + md5: 0937b0e88a24c82e02d5b714acc9957c + depends: + - __glibc >=2.17,<3.0.a0 + - geos >=3.14.1,<3.14.2.0a0 + - libgcc >=14 + - numpy >=1.21,<3 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/shapely?source=hash-mapping + size: 540156 + timestamp: 1762523617913 +- conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.2-py310h62de375_2.conda + sha256: ec86f7578ab42f26376dfffc1d402a92ea4f39c41b90551dd60639eb6fedd6a6 + md5: 35c16566728932aaaeea780588a77d52 + depends: + - geos >=3.14.1,<3.14.2.0a0 + - numpy >=1.21,<3 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/shapely?source=hash-mapping - size: 504054 - timestamp: 1747665013026 + size: 503715 + timestamp: 1762523745967 - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda sha256: 458227f759d5e3fcec5d9b7acce54e10c9e1f4f4b7ec978f3bfd54ce4ee9853d md5: 3339e3b65d58accf4ca4fb8748ab16b3 @@ -4024,7 +8641,7 @@ packages: license: MIT license_family: MIT purls: - - pkg:pypi/six?source=compressed-mapping + - pkg:pypi/six?source=hash-mapping size: 18455 timestamp: 1753199211006 - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.2-pyhd8ed1ab_0.conda @@ -4038,9 +8655,22 @@ packages: - pkg:pypi/smmap?source=hash-mapping size: 26051 timestamp: 1739781801801 -- conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_0.conda - sha256: b38ed597bf71f73275a192b8cb22888997760bac826321f5838951d5d31acb23 - md5: 194a0c548899fa2a10684c34e56a3564 +- conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda + sha256: 48f3f6a76c34b2cfe80de9ce7f2283ecb55d5ed47367ba91e8bb8104e12b8f11 + md5: 98b6c9dc80eb87b2519b97bcf7e578dd + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 45829 + timestamp: 1762948049098 +- conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda + sha256: d2deda1350abf8c05978b73cf7fe9147dd5c7f2f9b312692d1b98e52efad53c3 + md5: 3075846de68f942150069d4289aaad63 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -4051,28 +8681,91 @@ packages: license: BSD-3-Clause license_family: BSD purls: [] - size: 67221 - timestamp: 1753083479147 + size: 67417 + timestamp: 1762948090450 - pypi: https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl name: soupsieve version: '2.8' sha256: 0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c requires_python: '>=3.9' -- conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.50.4-hdb435a2_0.conda - sha256: 47717c9f78987a287984e89053cb8096457abd6b0fbf4cb39e63120797e2c993 - md5: b81e913bfad2759829f976fd926443af +- conda: https://conda.anaconda.org/conda-forge/linux-64/spirv-tools-2025.4-hb700be7_0.conda + sha256: aa0f0fc41646ef5a825d5725a2d06659df1c1084f15155936319e1909ac9cd16 + md5: aace50912e0f7361d0d223e7f7cfa6e5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - spirv-headers >=1.4.328.0,<1.4.328.1.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 2248062 + timestamp: 1759805790709 +- conda: https://conda.anaconda.org/conda-forge/win-64/spirv-tools-2025.4-h49e36cd_0.conda + sha256: 952a88cb050d8b21c020c03181af4ae8d89dd586631438cefbe66be6c15d6b92 + md5: 6e7df59eec517187e48699b298e750a2 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - spirv-headers >=1.4.328.0,<1.4.328.1.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 14158518 + timestamp: 1759806206089 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.51.0-heff268d_0.conda + sha256: 5cece58ca7353705ea47bbe44088baee70d2dfa8bdf2bbcd211698f60ab5e7cd + md5: 5422f0e1b59d2aa29329d5b3e36d57e5 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - libsqlite 3.51.0 hee844dc_0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - readline >=8.2,<9.0a0 + license: blessing + purls: [] + size: 182985 + timestamp: 1762299697693 +- conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.51.0-hdb435a2_0.conda + sha256: 95fb6af6ecc42df3c16e0155112bfe72f9be8d5a0cc323885b358ce23c213308 + md5: 442b64e528a1ee68ae90f11ba18570cd depends: - - libsqlite 3.50.4 hf5d6505_0 + - libsqlite 3.51.0 hf5d6505_0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: blessing purls: [] - size: 400981 - timestamp: 1753948927232 -- conda: https://conda.anaconda.org/conda-forge/win-64/statsmodels-0.14.5-py310h8f3aa81_0.conda - sha256: 3765bdbe6c13772d03e326696993378de37fdb197ea8a87355f2cd0f3d7d2ab1 - md5: 2fc7c4bf01384e0a2782086c84235247 + size: 400646 + timestamp: 1762299769405 +- conda: https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.5-py310hf779ad0_1.conda + sha256: 32bed514e77d591ae95770ed6242e33205ce05731d49314c3e5d7aaa01e50707 + md5: 60c940dc2baf2dac42ca194340681798 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - numpy <3,>=1.22.3 + - numpy >=1.21,<3 + - packaging >=21.3 + - pandas !=2.1.0,>=1.4 + - patsy >=0.5.6 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - scipy !=1.9.2,>=1.8 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/statsmodels?source=hash-mapping + size: 10615748 + timestamp: 1759297373333 +- conda: https://conda.anaconda.org/conda-forge/win-64/statsmodels-0.14.5-py310h8f3aa81_1.conda + sha256: f3b68419b3b237c4298d8697b00f0d0763c48a017db11c8f6cf0857fa422541c + md5: 4b8f6f40d86ade9f5d57d80b5e336ed2 depends: - numpy <3,>=1.22.3 - numpy >=1.21,<3 @@ -4089,14 +8782,26 @@ packages: license_family: BSD purls: - pkg:pypi/statsmodels?source=hash-mapping - size: 10295399 - timestamp: 1751918450126 -- pypi: https://files.pythonhosted.org/packages/f0/35/9c524281aab9550187d638862ceb663cb2d31fe04bb630aeb109faf81432/sumolib-1.24.0-py3-none-any.whl + size: 10072771 + timestamp: 1759297671065 +- pypi: https://files.pythonhosted.org/packages/1a/48/39dbcca267b826eaf7b0edc1f0e78f85698b4132393a894fe58656535927/sumolib-1.25.0-py3-none-any.whl name: sumolib - version: 1.24.0 - sha256: d4e8c0315dd240af60665cb7c0f34f876abf91daab73f53c17a5c8284c0acd5d + version: 1.25.0 + sha256: 32e431ba0737dc44f25916371900996e09d6dac51c93cd178fdb2c91c6aa18a9 requires_dist: - matplotlib ; extra == 'visualization' +- conda: https://conda.anaconda.org/conda-forge/linux-64/svt-av1-3.1.2-hecca717_0.conda + sha256: 34e2e9c505cd25dba0a9311eb332381b15147cf599d972322a7c197aedfc8ce2 + md5: 9859766c658e78fec9afa4a54891d920 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 2741200 + timestamp: 1756086702093 - conda: https://conda.anaconda.org/conda-forge/win-64/svt-av1-3.1.2-hac47afa_0.conda sha256: 444c94a9c1fcb2cdf78b260472451990257733bcf89ed80c73db36b5047d3134 md5: 91866412570c922f55178855deb0f952 @@ -4118,9 +8823,22 @@ packages: - pytest>=7.1.0 ; extra == 'dev' - hypothesis>=6.70.0 ; extra == 'dev' requires_python: '>=3.9' -- conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2021.13.0-h18a62a1_3.conda - sha256: 30e82640a1ad9d9b5bee006da7e847566086f8fdb63d15b918794a7ef2df862c - md5: 72226638648e494aaafde8155d50dab2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2022.3.0-h8d10470_1.conda + sha256: 2e3238234ae094d5a5f7c559410ea8875351b6bac0d9d0e576bf64b732b8029e + md5: e3259be3341da4bc06c5b7a78c8bf1bd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libhwloc >=2.12.1,<2.12.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 181262 + timestamp: 1762509955687 +- conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2021.13.0-hd094cb3_4.conda + sha256: 5b4618b9853919462aa185c1ea62cc5ff1d3b2a2215932b8330c087ffae7bdb8 + md5: dd78eb7b37991e650fec48b075bf5301 depends: - libhwloc >=2.12.1,<2.12.2.0a0 - ucrt >=10.0.20348.0 @@ -4129,8 +8847,59 @@ packages: license: Apache-2.0 license_family: APACHE purls: [] - size: 150266 - timestamp: 1755776172092 + size: 149964 + timestamp: 1762510496532 +- conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.20.0-pyhe01879c_0.conda + sha256: 7856949642db7b5d939622bd1702e365bf2cfc434e2e07f0ee88e5cacfd1a2f5 + md5: 37f7666c348f9f09378c3aefa90cb434 + depends: + - python >=3.9 + - absl-py >=0.4 + - grpcio >=1.48.2 + - markdown >=2.6.8 + - numpy >=1.12.0 + - packaging + - pillow + - protobuf >=3.19.6,!=4.24.0 + - werkzeug >=1.0.1 + - setuptools >=41.0.0 + - tensorboard-data-server >=0.7.0,<0.8.0 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/tensorboard?source=hash-mapping + size: 5250436 + timestamp: 1752825441697 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tensorboard-data-server-0.7.0-py310hed992bd_3.conda + sha256: bbaadcac7a5a1b801af844ee4fb32095d6a91a1327ca55912a24edcd5d3feb43 + md5: 4af6cb6ac036b31b5202f97a4ea769ca + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - openssl >=3.5.4,<4.0a0 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/tensorboard-data-server?source=hash-mapping + size: 3499099 + timestamp: 1759413501359 +- conda: https://conda.anaconda.org/conda-forge/win-64/tensorboard-data-server-0.7.0-py310h5588dad_3.conda + sha256: 5fa52aa38a4ba6b89f42694c18906b1bcf9a9fcd03739971387bdc9d45d2a04c + md5: d2c6b74aa1a3c530aa7adf7627daf5ca + depends: + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/tensorboard-data-server?source=hash-mapping + size: 18095 + timestamp: 1759413262378 - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda sha256: 6016672e0e72c4cf23c0cf7b1986283bd86a9c17e8d319212d78d8e9ae42fdfd md5: 9d64911b31d57ca443e9f1e36b04385f @@ -4142,6 +8911,17 @@ packages: - pkg:pypi/threadpoolctl?source=hash-mapping size: 23869 timestamp: 1741878358548 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tinyobjloader-1.0.7-h59595ed_2.conda + sha256: 72628067e594f1431968143aed6a5d4e6f0f5d6d1aac0c5165b04143b4f03119 + md5: 3d75225b64bcbdc86128e693104a409d + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + purls: [] + size: 71746 + timestamp: 1704450371417 - conda: https://conda.anaconda.org/conda-forge/win-64/tinyobjloader-1.0.7-h63175ca_2.conda sha256: 6f280eb3a40c9135ec92b6ba9dcbd38aee42caeba8e71f5e03d9fdfd9320948d md5: dd7d0d5ef75feeef66a09dbfe8ecdc73 @@ -4154,9 +8934,23 @@ packages: purls: [] size: 138948 timestamp: 1704450827319 -- conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_2.conda - sha256: e3614b0eb4abcc70d98eae159db59d9b4059ed743ef402081151a948dce95896 - md5: ebd0e761de9aa879a51d22cc721bd095 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda + sha256: 1544760538a40bcd8ace2b1d8ebe3eb5807ac268641f8acdc18c69c5ebfeaf64 + md5: 86bc20552bf46075e3d92b67f089172d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + constrains: + - xorg-libx11 >=1.8.12,<2.0a0 + license: TCL + license_family: BSD + purls: [] + size: 3284905 + timestamp: 1763054914403 +- conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.conda + sha256: 4581f4ffb432fefa1ac4f85c5682cc27014bcd66e7beaa0ee330e927a7858790 + md5: 7cb36e506a7dba4817970f8adb6396f9 depends: - ucrt >=10.0.20348.0 - vc >=14.2,<15 @@ -4164,11 +8958,42 @@ packages: license: TCL license_family: BSD purls: [] - size: 3466348 - timestamp: 1748388121356 -- pypi: https://download.pytorch.org/whl/nightly/cu128/torch-2.9.0.dev20250827%2Bcu128-cp310-cp310-win_amd64.whl + size: 3472313 + timestamp: 1763055164278 +- pypi: https://download.pytorch.org/whl/cu130/torch-2.9.1%2Bcu130-cp310-cp310-manylinux_2_28_x86_64.whl + name: torch + version: 2.9.1+cu130 + requires_dist: + - filelock + - typing-extensions>=4.10.0 + - setuptools ; python_full_version >= '3.12' + - sympy>=1.13.3 + - networkx>=2.5.1 + - jinja2 + - fsspec>=0.8.5 + - nvidia-cuda-nvrtc==13.0.48 ; sys_platform == 'linux' + - nvidia-cuda-runtime==13.0.48 ; sys_platform == 'linux' + - nvidia-cuda-cupti==13.0.48 ; sys_platform == 'linux' + - nvidia-cudnn-cu13==9.13.0.50 ; sys_platform == 'linux' + - nvidia-cublas==13.0.0.19 ; sys_platform == 'linux' + - nvidia-cufft==12.0.0.15 ; sys_platform == 'linux' + - nvidia-curand==10.4.0.35 ; sys_platform == 'linux' + - nvidia-cusolver==12.0.3.29 ; sys_platform == 'linux' + - nvidia-cusparse==12.6.2.49 ; sys_platform == 'linux' + - nvidia-cusparselt-cu13==0.8.0 ; sys_platform == 'linux' + - nvidia-nccl-cu13==2.27.7 ; sys_platform == 'linux' + - nvidia-nvshmem-cu13==3.3.24 ; sys_platform == 'linux' + - nvidia-nvtx==13.0.39 ; sys_platform == 'linux' + - nvidia-nvjitlink==13.0.39 ; sys_platform == 'linux' + - nvidia-cufile==1.15.0.42 ; sys_platform == 'linux' + - triton==3.5.1 ; sys_platform == 'linux' + - optree>=0.13.0 ; extra == 'optree' + - opt-einsum>=3.3 ; extra == 'opt-einsum' + - pyyaml ; extra == 'pyyaml' + requires_python: '>=3.10' +- pypi: https://download.pytorch.org/whl/nightly/cu128/torch-2.10.0.dev20251124%2Bcu128-cp310-cp310-win_amd64.whl name: torch - version: 2.9.0.dev20250827+cu128 + version: 2.10.0.dev20251124+cu128 requires_dist: - filelock - typing-extensions>=4.10.0 @@ -4180,20 +9005,45 @@ packages: - opt-einsum>=3.3 ; extra == 'opt-einsum' - optree>=0.13.0 ; extra == 'optree' - pyyaml ; extra == 'pyyaml' - requires_python: '>=3.9' -- pypi: https://download.pytorch.org/whl/nightly/cu128/torchvision-0.24.0.dev20250827%2Bcu128-cp310-cp310-win_amd64.whl + requires_python: '>=3.10' +- pypi: https://download.pytorch.org/whl/cu130/torchvision-0.24.1%2Bcu130-cp310-cp310-manylinux_2_28_x86_64.whl name: torchvision - version: 0.24.0.dev20250827+cu128 + version: 0.24.1+cu130 + sha256: f6e7a6cb4af75dc378fceba8ef78697b6fd8f90feab302790f6755155d0ab7df + requires_dist: + - numpy + - torch==2.9.1 + - pillow>=5.3.0,!=8.3.* + - gdown>=4.7.3 ; extra == 'gdown' + - scipy ; extra == 'scipy' + requires_python: '>=3.10' +- pypi: https://download.pytorch.org/whl/nightly/cu128/torchvision-0.25.0.dev20251124%2Bcu128-cp310-cp310-win_amd64.whl + name: torchvision + version: 0.25.0.dev20251124+cu128 requires_dist: - numpy - torch - pillow>=5.3.0,!=8.3.* - gdown>=4.7.3 ; extra == 'gdown' - scipy ; extra == 'scipy' - requires_python: '>=3.9' -- conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.2-py310h29418f3_0.conda - sha256: f87dbe5c74811d3466470ce9dbd8a5c27c6d2556b4967eae4cdba9fa0fbdef1a - md5: 976f9142074884ea8f1d59806ad5fc21 + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py310h7c4b9e2_2.conda + sha256: 1509c061d22223367da086b44dcade7fd81704fe97e6c9fc6020776ddf0430b5 + md5: 8957cd12e994a03899291a07cf31e5f1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/tornado?source=hash-mapping + size: 663141 + timestamp: 1762506823343 +- conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.2-py310h29418f3_2.conda + sha256: 1e815a9ca79e4c2da8403bbee739f8e37870b6221c666ddd976892c4030bbd24 + md5: 1fb2064414b72f8486952e0ad3463b4f depends: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 @@ -4204,19 +9054,28 @@ packages: license_family: Apache purls: - pkg:pypi/tornado?source=hash-mapping - size: 663851 - timestamp: 1754732410256 -- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda - sha256: 11e2c85468ae9902d24a27137b6b39b4a78099806e551d390e394a8c34b48e40 - md5: 9efbfdc37242619130ea42b1cc4ed861 - depends: - - colorama - - python >=3.9 - license: MPL-2.0 or MIT - purls: - - pkg:pypi/tqdm?source=hash-mapping - size: 89498 - timestamp: 1735661472632 + size: 665198 + timestamp: 1762507149980 +- pypi: https://files.pythonhosted.org/packages/fd/6e/676ab5019b4dde8b9b7bab71245102fc02778ef3df48218b298686b9ffd6/triton-3.5.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: triton + version: 3.5.1 + sha256: 5fc53d849f879911ea13f4a877243afc513187bc7ee92d1f2c0f1ba3169e3c94 + requires_dist: + - importlib-metadata ; python_full_version < '3.10' + - cmake>=3.20,<4.0 ; extra == 'build' + - lit ; extra == 'build' + - autopep8 ; extra == 'tests' + - isort ; extra == 'tests' + - numpy ; extra == 'tests' + - pytest ; extra == 'tests' + - pytest-forked ; extra == 'tests' + - pytest-xdist ; extra == 'tests' + - scipy>=1.7.1 ; extra == 'tests' + - llnl-hatchet ; extra == 'tests' + - matplotlib ; extra == 'tutorials' + - pandas ; extra == 'tutorials' + - tabulate ; extra == 'tutorials' + requires_python: '>=3.10,<3.15' - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda sha256: 7c2df5721c742c2a47b2c8f960e718c930031663ac1174da67c1ed5999f7938c md5: edd329d7d3a4ab45dcf905899a7a6115 @@ -4236,7 +9095,7 @@ packages: license: PSF-2.0 license_family: PSF purls: - - pkg:pypi/typing-extensions?source=compressed-mapping + - pkg:pypi/typing-extensions?source=hash-mapping size: 51692 timestamp: 1756220668932 - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda @@ -4246,24 +9105,25 @@ packages: purls: [] size: 122968 timestamp: 1742727099393 -- conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.22621.0-h57928b3_1.conda - sha256: db8dead3dd30fb1a032737554ce91e2819b43496a0db09927edf01c32b577450 - md5: 6797b005cd0f439c4c5c9ac565783700 +- conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda + sha256: 3005729dce6f3d3f5ec91dfc49fc75a0095f9cd23bab49efb899657297ac91a5 + md5: 71b24316859acd00bdb8b38f5e2ce328 constrains: + - vc14_runtime >=14.29.30037 - vs2015_runtime >=14.29.30037 license: LicenseRef-MicrosoftWindowsSDK10 purls: [] - size: 559710 - timestamp: 1728377334097 -- conda: https://conda.anaconda.org/conda-forge/noarch/ultralytics-8.3.186-pyh2a12c56_0.conda - sha256: d915e974e8908105471e29922f77d2ebffc26767b38d88a38d04a2271ec554c7 - md5: 1c91a0fe7f861fa341b823b4699b9dd4 + size: 694692 + timestamp: 1756385147981 +- conda: https://conda.anaconda.org/conda-forge/noarch/ultralytics-8.3.232-pyh865f8e1_0.conda + sha256: 0446807566edfa83111c913d46b1157990f68e14d9eb100c439c189a42944b31 + md5: 63bfa142dae81bca17d5c5e68c554653 depends: - matplotlib-base >=3.3.0 - numpy >=1.22.2 - opencv >=4.6.0 - - pandas >=1.1.4 - pillow >=7.1.2 + - polars - psutil - py-cpuinfo - python >=3.10 @@ -4271,28 +9131,41 @@ packages: - requests >=2.23.0 - scipy >=1.4.1 - seaborn >=0.11.0 - - tqdm >=4.64.0 license: AGPL-3.0-only license_family: AGPL purls: - pkg:pypi/ultralytics?source=hash-mapping - size: 788983 - timestamp: 1756161286716 -- conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-16.0.0-py310ha8f682b_0.conda - sha256: b59837c68d8edcca3c86c205a8c5dec63356029e48d55ed88c5483105d73ac0c - md5: b28aead44c6e19a1fbba7752aa242b34 + size: 804670 + timestamp: 1764072333737 +- conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.0-py310h7c4b9e2_1.conda + sha256: cffe509e0294586fbcee9cbb762d6144636c5d4a19defffda9f9c726a84b55e7 + md5: b1ccdb989be682ab0dd430c1c15d5012 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/unicodedata2?source=hash-mapping + size: 409991 + timestamp: 1763054811367 +- conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.0-py310h29418f3_1.conda + sha256: 43c75e924ec25549c0080cfcf9906bd0a2903dfd0710d1ea9b1583e7834818e1 + md5: fcbbbdf7b8ebd24940f15af0fb52562e depends: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/unicodedata2?source=hash-mapping - size: 400554 - timestamp: 1736692785793 + size: 405522 + timestamp: 1763055146576 - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda sha256: 4fb9789154bd666ca74e428d973df81087a697dbb987775bc3198d2215f240f8 md5: 436c165519e140cb08d246a4472a9d6a @@ -4308,93 +9181,156 @@ packages: - pkg:pypi/urllib3?source=hash-mapping size: 101735 timestamp: 1750271478254 -- conda: https://conda.anaconda.org/conda-forge/win-64/utfcpp-4.0.6-hc1507ef_0.conda - sha256: 71ee67c739bb32a2b684231f156150e1f7fd6c852aa2ceaae50e56909c073227 - md5: 7071f524e58d346948d4ac7ae7b5d2f2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/utfcpp-4.0.8-ha770c72_0.conda + sha256: bbfbfc43bc028ec8acc5c9c2bb9a52c7652140cef91fdb6219a52d91d773a474 + md5: a480ee3eb9c95364a229673a28384899 + license: BSL-1.0 + purls: [] + size: 14169 + timestamp: 1758003868824 +- conda: https://conda.anaconda.org/conda-forge/win-64/utfcpp-4.0.8-h57928b3_0.conda + sha256: ea90a0769e79ee9943d6a23b7c83a505512718ffa24377a42677b332c8885a5f + md5: 52d910cbb6a915455a0fd55d82b01b7d license: BSL-1.0 purls: [] - size: 13983 - timestamp: 1730672186474 -- conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h41ae7f8_31.conda - sha256: cb357591d069a1e6cb74199a8a43a7e3611f72a6caed9faa49dbb3d7a0a98e0b - md5: 28f4ca1e0337d0f27afb8602663c5723 + size: 14683 + timestamp: 1758003886472 +- conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h2b53caa_32.conda + sha256: 82250af59af9ff3c6a635dd4c4764c631d854feb334d6747d356d949af44d7cf + md5: ef02bbe151253a72b8eda264a935db66 depends: - - vc14_runtime >=14.44.35208 + - vc14_runtime >=14.42.34433 track_features: - vc14 license: BSD-3-Clause license_family: BSD purls: [] - size: 18249 - timestamp: 1753739241465 -- conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_31.conda - sha256: af4b4b354b87a9a8d05b8064ff1ea0b47083274f7c30b4eb96bc2312c9b5f08f - md5: 603e41da40a765fd47995faa021da946 + size: 18861 + timestamp: 1760418772353 +- conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_32.conda + sha256: e3a3656b70d1202e0d042811ceb743bd0d9f7e00e2acdf824d231b044ef6c0fd + md5: 378d5dcec45eaea8d303da6f00447ac0 depends: - ucrt >=10.0.20348.0 - - vcomp14 14.44.35208 h818238b_31 + - vcomp14 14.44.35208 h818238b_32 constrains: - - vs2015_runtime 14.44.35208.* *_31 + - vs2015_runtime 14.44.35208.* *_32 license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime license_family: Proprietary purls: [] - size: 682424 - timestamp: 1753739239305 -- conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_31.conda - sha256: 67b317b64f47635415776718d25170a9a6f9a1218c0f5a6202bfd687e07b6ea4 - md5: a6b1d5c1fc3cb89f88f7179ee6a9afe3 + size: 682706 + timestamp: 1760418629729 +- conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_32.conda + sha256: f3790c88fbbdc55874f41de81a4237b1b91eab75e05d0e58661518ff04d2a8a1 + md5: 58f67b437acbf2764317ba273d731f1d depends: - ucrt >=10.0.20348.0 constrains: - - vs2015_runtime 14.44.35208.* *_31 + - vs2015_runtime 14.44.35208.* *_32 license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime license_family: Proprietary purls: [] - size: 113963 - timestamp: 1753739198723 -- conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.44.35208-h38c0c73_31.conda - sha256: 8b20152d00e1153ccb1ed377a160110482f286a6d85a82b57ffcd60517d523a7 - md5: d75abcfbc522ccd98082a8c603fce34c + size: 114846 + timestamp: 1760418593847 +- conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.44.35208-h38c0c73_32.conda + sha256: 65cea43f4de99bc81d589e746c538908b2e95aead9042fecfbc56a4d14684a87 + md5: dfc1e5bbf1ecb0024a78e4e8bd45239d depends: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD purls: [] - size: 18249 - timestamp: 1753739241918 -- conda: https://conda.anaconda.org/conda-forge/win-64/vtk-base-9.4.2-py310h27c0482_3.conda - sha256: 2be3baee02c6b4d178bdfdff0fd5c2b434519ff1d0ded0abfe4c3ffe7565c649 - md5: 50b9761e50e97fd99a07040872ed34b0 + size: 18919 + timestamp: 1760418632059 +- conda: https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.1-py310h991dc19_7.conda + sha256: cf4a5f52c6bb5d751281fb3f36aae43e8455ac01bb028957f7d499a73fdc3dd4 + md5: 842392f939204a15953bf32d28386335 depends: + - __glibc >=2.17,<3.0.a0 + - cli11 - double-conversion >=3.3.1,<3.4.0a0 - - ffmpeg >=7.1.1,<8.0a0 - fmt >=11.2.0,<11.3.0a0 - gl2ps >=1.4.2,<1.4.3.0a0 - hdf5 >=1.14.6,<1.14.7.0a0 - jsoncpp >=1.9.6,<1.9.7.0a0 - libexpat >=2.7.1,<3.0a0 - - libfreetype >=2.13.3 - - libfreetype6 >=2.13.3 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libglu >=9.0.3,<9.1.0a0 + - libglvnd >=1.7.0,<2.0a0 + - libglx >=1.7.0,<2.0a0 - libjpeg-turbo >=3.1.0,<4.0a0 - liblzma >=5.8.1,<6.0a0 - - libnetcdf >=4.9.2,<4.9.3.0a0 + - libnetcdf >=4.9.3,<4.9.4.0a0 - libogg >=1.3.5,<1.4.0a0 + - libopengl >=1.7.0,<2.0a0 - libpng >=1.6.50,<1.7.0a0 - - libsqlite >=3.50.3,<4.0a0 + - libsqlite >=3.50.4,<4.0a0 + - libstdcxx >=14 - libtheora >=1.1.1,<1.2.0a0 - - libtiff >=4.7.0,<4.8.0a0 - - libxml2 >=2.13.8,<2.14.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.1,<2.0a0 + - loguru + - lz4-c >=1.10.0,<1.11.0a0 + - matplotlib-base >=2.0.0 + - nlohmann_json + - numpy + - proj >=9.7.0,<9.8.0a0 + - pugixml >=1.15,<1.16.0a0 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - qt6-main >=6.9.3,<6.10.0a0 + - tbb >=2021.13.0 + - utfcpp + - wslink + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + constrains: + - libboost-headers >=1.88.0,<1.89.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/vtk?source=hash-mapping + size: 63053075 + timestamp: 1760144794843 +- conda: https://conda.anaconda.org/conda-forge/win-64/vtk-base-9.5.1-py310hcc07014_7.conda + sha256: 317ccba99e038c57f7bec711884e196d922dd5f932965a13c4f53e16ab7aad2d + md5: db08a562305bc05e484104ded982e907 + depends: + - cli11 + - double-conversion >=3.3.1,<3.4.0a0 + - ffmpeg >=8.0.0,<9.0a0 + - fmt >=11.2.0,<11.3.0a0 + - gl2ps >=1.4.2,<1.4.3.0a0 + - hdf5 >=1.14.6,<1.14.7.0a0 + - jsoncpp >=1.9.6,<1.9.7.0a0 + - libexpat >=2.7.1,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblzma >=5.8.1,<6.0a0 + - libnetcdf >=4.9.3,<4.9.4.0a0 + - libogg >=1.3.5,<1.4.0a0 + - libpng >=1.6.50,<1.7.0a0 + - libsqlite >=3.50.4,<4.0a0 + - libtheora >=1.1.1,<1.2.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libxml2 + - libxml2-16 >=2.14.6 - libzlib >=1.3.1,<2.0a0 - loguru - lz4-c >=1.10.0,<1.11.0a0 - matplotlib-base >=2.0.0 - nlohmann_json - numpy - - proj >=9.6.2,<9.7.0a0 + - proj >=9.7.0,<9.8.0a0 - pugixml >=1.15,<1.16.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - - qt6-main >=6.9.1,<6.10.0a0 + - qt6-main >=6.9.3,<6.10.0a0 - tbb >=2021.13.0 - ucrt >=10.0.20348.0 - utfcpp @@ -4402,17 +9338,29 @@ packages: - vc14_runtime >=14.44.35208 - wslink constrains: - - paraview ==9999999999 - libboost-headers >=1.88.0,<1.89.0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/vtk?source=hash-mapping - size: 40479235 - timestamp: 1753504081093 -- conda: https://conda.anaconda.org/conda-forge/win-64/watchdog-6.0.0-py310h5588dad_1.conda - sha256: 1dab87f1e0cce4b9a42fc8cd0bef71e5c878de5b10ea34d1d998c179f0647c51 - md5: 5df1364f70e4114ebb5d4157d492d17c + size: 44677598 + timestamp: 1760153465451 +- conda: https://conda.anaconda.org/conda-forge/linux-64/watchdog-6.0.0-py310hff52083_2.conda + sha256: 56c31d19bae3d63eaba45c3fb769fec6ceca73d83551ee95174b62f20ff6b513 + md5: 3372abc086369214a66fcfd2d232bd47 + depends: + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - pyyaml >=3.10 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/watchdog?source=hash-mapping + size: 115863 + timestamp: 1763021771394 +- conda: https://conda.anaconda.org/conda-forge/win-64/watchdog-6.0.0-py310h5588dad_2.conda + sha256: 08e216060a10011aa84877a0770b9e65e52953b060fd4a0697e168c2557a78ef + md5: fe70e4d9ec05710bfd56186d7f860a47 depends: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 @@ -4421,8 +9369,30 @@ packages: license_family: APACHE purls: - pkg:pypi/watchdog?source=hash-mapping - size: 141890 - timestamp: 1756135516164 + size: 141225 + timestamp: 1763022153349 +- conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda + sha256: 3aa04ae8e9521d9b56b562376d944c3e52b69f9d2a0667f77b8953464822e125 + md5: 035da2e4f5770f036ff704fa17aace24 + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat >=2.7.1,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 329779 + timestamp: 1761174273487 +- conda: https://conda.anaconda.org/conda-forge/noarch/wayland-protocols-1.46-hd8ed1ab_0.conda + sha256: 8094050a5146dadc4e94d71351b70ec0f54803ef3999afa6640e599a0b3b43a8 + md5: 967e4d37eaad18d4add66aaa394d8de8 + license: MIT + license_family: MIT + purls: [] + size: 139554 + timestamp: 1764021418156 - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.3-pyhd8ed1ab_1.conda sha256: cd9a603beae0b237be7d9dfae8ae0b36ad62666ac4bb073969bce7da6f55157c md5: 0a9b57c159d56b508613cc39022c1b9e @@ -4468,19 +9438,29 @@ packages: - pkg:pypi/win-inet-pton?source=hash-mapping size: 9555 timestamp: 1733130678956 -- conda: https://conda.anaconda.org/conda-forge/noarch/wslink-2.4.0-pyhd8ed1ab_0.conda - sha256: 0f7258a383db60fb8563eb64df13c0df1c4c6cdcdb3428a06f3ef4f562fc5beb - md5: a7c17eeb817efebaf59a48fdeab284a4 +- conda: https://conda.anaconda.org/conda-forge/noarch/wslink-2.5.0-pyhd8ed1ab_0.conda + sha256: e9ac3caa3b17bed9bc301a67d3950f84fa37fb34002d2878c46cafb87978401d + md5: 8fa415e696acd9af59ce0a4425fd1b38 depends: - aiohttp <4 - msgpack-python >=1,<2 - - python >=3.9 + - python >=3.10 license: BSD-3-Clause license_family: BSD purls: - - pkg:pypi/wslink?source=compressed-mapping - size: 35820 - timestamp: 1755596457702 + - pkg:pypi/wslink?source=hash-mapping + size: 35839 + timestamp: 1760984848678 +- conda: https://conda.anaconda.org/conda-forge/linux-64/x264-1!164.3095-h166bdaf_2.tar.bz2 + sha256: 175315eb3d6ea1f64a6ce470be00fa2ee59980108f246d3072ab8b977cb048a5 + md5: 6c99772d483f566d59e25037fea2c4b1 + depends: + - libgcc-ng >=12 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 897548 + timestamp: 1660323080555 - conda: https://conda.anaconda.org/conda-forge/win-64/x264-1!164.3095-h8ffe710_2.tar.bz2 sha256: 97166b318f8c68ffe4d50b2f4bd36e415219eeaef233e7d41c54244dc6108249 md5: 19e39905184459760ccb8cf5c75f148b @@ -4492,6 +9472,17 @@ packages: purls: [] size: 1041889 timestamp: 1660323726084 +- conda: https://conda.anaconda.org/conda-forge/linux-64/x265-3.5-h924138e_3.tar.bz2 + sha256: 76c7405bcf2af639971150f342550484efac18219c0203c5ee2e38b8956fe2a0 + md5: e7f6ed84d4623d52ee581325c1587a6b + depends: + - libgcc-ng >=10.3.0 + - libstdcxx-ng >=10.3.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 3357188 + timestamp: 1646609687141 - conda: https://conda.anaconda.org/conda-forge/win-64/x265-3.5-h2d74725_3.tar.bz2 sha256: 02b9874049112f2b7335c9a3e880ac05d99a08d9a98160c5a98898b2b3ac42b2 md5: ca7129a334198f08347fb19ac98a2de9 @@ -4503,30 +9494,342 @@ packages: purls: [] size: 5517425 timestamp: 1646611941216 -- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-h0e40799_0.conda - sha256: 047836241b2712aab1e29474a6f728647bff3ab57de2806b0bb0a6cf9a2d2634 - md5: 2ffbfae4548098297c033228256eb96e +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + sha256: ad8cab7e07e2af268449c2ce855cbb51f43f4664936eff679b1f3862e6e4b01d + md5: fdc27cb255a7a2cc73b7919a968b48f0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libxcb >=1.17.0,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 20772 + timestamp: 1750436796633 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + sha256: c2be9cae786fdb2df7c2387d2db31b285cf90ab3bfabda8fa75a596c3d20fc67 + md5: 4d1fc190b99912ed557a8236e958c559 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxcb >=1.13 + - libxcb >=1.17.0,<2.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 20829 + timestamp: 1763366954390 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + sha256: 94b12ff8b30260d9de4fd7a28cca12e028e572cbc504fd42aa2646ec4a5bded7 + md5: a0901183f08b6c7107aab109733a3c91 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + - xcb-util >=0.4.1,<0.5.0a0 + license: MIT + license_family: MIT + purls: [] + size: 24551 + timestamp: 1718880534789 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + sha256: 546e3ee01e95a4c884b6401284bb22da449a2f4daf508d038fdfa0712fe4cc69 + md5: ad748ccca349aec3e91743e08b5e2b50 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 14314 + timestamp: 1718846569232 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + sha256: 2d401dadc43855971ce008344a4b5bd804aca9487d8ebd83328592217daca3df + md5: 0e0cbe0564d03a99afd5fd7b362feecd + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 16978 + timestamp: 1718848865819 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + sha256: 31d44f297ad87a1e6510895740325a635dd204556aa7e079194a0034cdd7e66a + md5: 608e0ef8256b81d04456e8d211eee3e8 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 51689 + timestamp: 1718844051451 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.46-hb03c661_0.conda + sha256: aa03b49f402959751ccc6e21932d69db96a65a67343765672f7862332aa32834 + md5: 71ae752a748962161b4740eaff510258 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 396975 + timestamp: 1759543819846 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + sha256: c12396aabb21244c212e488bbdc4abcdef0b7404b15761d9329f5a4a39113c4b + md5: fb901ff28063514abb6046c9ec2c4a45 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 58628 + timestamp: 1734227592886 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda + sha256: 277841c43a39f738927145930ff963c5ce4c4dacf66637a3d95d802a64173250 + md5: 1c74ff8c35dcadf952a16f752ca5aa49 depends: + - __glibc >=2.17,<3.0.a0 - libgcc >=13 + - libuuid >=2.38.1,<3.0a0 + - xorg-libice >=1.1.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 27590 + timestamp: 1741896361728 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda + sha256: 51909270b1a6c5474ed3978628b341b4d4472cd22610e5f22b506855a5e20f67 + md5: db038ce880f100acc74dba10302b5630 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libxcb >=1.17.0,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 835896 + timestamp: 1741901112627 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + sha256: 6bc6ab7a90a5d8ac94c7e300cc10beb0500eeba4b99822768ca2f2ef356f731b + md5: b2895afaf55bf96a8c8282a2e47a5de0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 15321 + timestamp: 1762976464266 +- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_1.conda + sha256: 156a583fa43609507146de1c4926172286d92458c307bb90871579601f6bc568 + md5: 8436cab9a76015dfe7208d3c9f97c156 + depends: + - libgcc >=14 - libwinpthread >=12.0.0.r4.gg4f2fc60ca - ucrt >=10.0.20348.0 license: MIT license_family: MIT purls: [] - size: 108013 - timestamp: 1734229474049 -- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-h0e40799_0.conda - sha256: 9075f98dcaa8e9957e4a3d9d30db05c7578a536950a31c200854c5c34e1edb2c - md5: 8393c0f7e7870b4eb45553326f81f0ff + size: 109246 + timestamp: 1762977105140 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda + sha256: 753f73e990c33366a91fd42cc17a3d19bb9444b9ca5ff983605fa9e953baf57f + md5: d3c295b50f092ab525ffe3c2aa4b7413 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 13603 + timestamp: 1727884600744 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + sha256: 832f538ade441b1eee863c8c91af9e69b356cd3e9e1350fff4fe36cc573fc91a + md5: 2ccd714aa2242315acaf0a67faea780b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + - xorg-libxrender >=0.9.11,<0.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 32533 + timestamp: 1730908305254 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + sha256: 43b9772fd6582bf401846642c4635c47a9b0e36ca08116b3ec3df36ab96e0ec0 + md5: b5fcc7172d22516e1f965490e65e33a4 depends: + - __glibc >=2.17,<3.0.a0 - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 13217 + timestamp: 1727891438799 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda + sha256: 25d255fb2eef929d21ff660a0c687d38a6d2ccfbcbf0cc6aa738b12af6e9d142 + md5: 1dafce8548e38671bea82e3f5c6ce22f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 20591 + timestamp: 1762976546182 +- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_1.conda + sha256: 366b8ae202c3b48958f0b8784bbfdc37243d3ee1b1cd4b8e76c10abe41fa258b + md5: a7c03e38aa9c0e84d41881b9236eacfb + depends: + - libgcc >=14 - libwinpthread >=12.0.0.r4.gg4f2fc60ca - ucrt >=10.0.20348.0 license: MIT license_family: MIT purls: [] - size: 69920 - timestamp: 1727795651979 + size: 70691 + timestamp: 1762977015220 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda + sha256: da5dc921c017c05f38a38bd75245017463104457b63a1ce633ed41f214159c14 + md5: febbab7d15033c913d53c7a2c102309d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 50060 + timestamp: 1727752228921 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda + sha256: 83c4c99d60b8784a611351220452a0a85b080668188dce5dfa394b723d7b64f4 + md5: ba231da7fccf9ea1e768caf5c7099b84 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 20071 + timestamp: 1759282564045 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda + sha256: 1a724b47d98d7880f26da40e45f01728e7638e6ec69f35a3e11f92acd05f9e7a + md5: 17dcc85db3c7886650b8908b183d6876 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 47179 + timestamp: 1727799254088 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda + sha256: 1b9141c027f9d84a9ee5eb642a0c19457c788182a5a73c5a9083860ac5c20a8c + md5: 5e2eb9bf77394fc2e5918beefec9f9ab + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 13891 + timestamp: 1727908521531 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda + sha256: ac0f037e0791a620a69980914a77cb6bb40308e26db11698029d6708f5aa8e0d + md5: 2de7f99d6581a4a7adbff607b5c278ca + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrender >=0.9.11,<0.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 29599 + timestamp: 1727794874300 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda + sha256: 044c7b3153c224c6cedd4484dd91b389d2d7fd9c776ad0f4a34f099b3389f4a1 + md5: 96d57aba173e878a2089d5638016dc5e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 33005 + timestamp: 1734229037766 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxscrnsaver-1.2.4-hb9d3cd8_0.conda + sha256: 58e8fc1687534124832d22e102f098b5401173212ac69eb9fd96b16a3e2c8cb2 + md5: 303f7a0e9e0cd7d250bb6b952cecda90 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 14412 + timestamp: 1727899730073 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + sha256: 752fdaac5d58ed863bbf685bb6f98092fe1a488ea8ebb7ed7b606ccfce08637a + md5: 7bbe9a0cc0df0ac5f5a8ad6d6a11af2f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxi >=1.7.10,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 32808 + timestamp: 1727964811275 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda + sha256: 8a4e2ee642f884e6b78c20c0892b85dd9b2a6e64a6044e903297e616be6ca35b + md5: 5efa5fa6243a622445fdfd72aee15efa + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 17819 + timestamp: 1734214575628 +- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + sha256: 6d9ea2f731e284e9316d95fa61869fe7bbba33df7929f82693c121022810f4ad + md5: a77f85f77be52ff59391544bfe73390a + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + purls: [] + size: 85189 + timestamp: 1753484064210 - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda sha256: 80ee68c1e7683a35295232ea79bcc87279d31ffeda04a1665efdb43cbd50a309 md5: 433699cba6602098ae8957a323da2664 @@ -4542,38 +9845,73 @@ packages: purls: [] size: 63944 timestamp: 1753484092156 -- conda: https://conda.anaconda.org/conda-forge/win-64/yarl-1.20.1-py310h38315fa_0.conda - sha256: b796574bcfd8deaf442a5ccd5cd080f5b69a913a76a30204ccda6ee62ec45f4d - md5: e4655763835ba8dddaf30014e6268217 +- conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.22.0-py310h3406613_0.conda + sha256: b6e527196d2ce27417721cb9540d1efa2614bad76c9fbd2334b4cd39ddbae364 + md5: ac707c966a3aa0c494ac763df31fa873 depends: + - __glibc >=2.17,<3.0.a0 - idna >=2.0 + - libgcc >=14 - multidict >=4.0 - propcache >=0.2.1 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/yarl?source=hash-mapping - size: 128584 - timestamp: 1749555387495 -- conda: https://conda.anaconda.org/conda-forge/win-64/zeromq-4.3.5-ha9f60a1_7.conda - sha256: 15cc8e2162d0a33ffeb3f7b7c7883fd830c54a4b1be6a4b8c7ee1f4fef0088fb - md5: e03f2c245a5ee6055752465519363b1c + size: 137784 + timestamp: 1761337034085 +- conda: https://conda.anaconda.org/conda-forge/win-64/yarl-1.22.0-py310hdb0e946_0.conda + sha256: 787d8d4e84655c9f6a62b7aaeb921df1d5359e64faf3a2cabbc0160047b05709 + md5: 502dec44c12027cf5dd00ed11f4f2444 depends: - - krb5 >=1.21.3,<1.22.0a0 + - idna >=2.0 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.10,<3.11.0a0 + - python_abi 3.10.* *_cp310 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/yarl?source=hash-mapping + size: 129944 + timestamp: 1761337641349 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda + sha256: 47cfe31255b91b4a6fa0e9dbaf26baa60ac97e033402dbc8b90ba5fee5ffe184 + md5: 8035e5b54c08429354d5d64027041cad + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 - libsodium >=1.0.20,<1.0.21.0a0 + - krb5 >=1.21.3,<1.22.0a0 + license: MPL-2.0 + license_family: MOZILLA + purls: [] + size: 310648 + timestamp: 1757370847287 +- conda: https://conda.anaconda.org/conda-forge/win-64/zeromq-4.3.5-h5bddc39_9.conda + sha256: 690cf749692c8ea556646d1a47b5824ad41b2f6dfd949e4cdb6c44a352fcb1aa + md5: a6c8f8ee856f7c3c1576e14b86cd8038 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libsodium >=1.0.20,<1.0.21.0a0 + - krb5 >=1.21.3,<1.22.0a0 license: MPL-2.0 license_family: MOZILLA purls: [] - size: 2527503 - timestamp: 1731585151036 + size: 265212 + timestamp: 1757370864284 - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda sha256: 7560d21e1b021fd40b65bfb72f67945a3fcb83d78ad7ccf37b8b3165ec3b68ad md5: df5e78d904988eb55042c0c97446079f @@ -4585,6 +9923,18 @@ packages: - pkg:pypi/zipp?source=hash-mapping size: 22963 timestamp: 1749421737203 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda + sha256: 5d7c0e5f0005f74112a34a7425179f4eb6e73c92f5d109e6af4ddeca407c92ab + md5: c9f075ab2f33b3bbee9e62d4ad0a6cd8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib 1.3.1 hb9d3cd8_2 + license: Zlib + license_family: Other + purls: [] + size: 92286 + timestamp: 1727963153079 - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.1-h2466b09_2.conda sha256: 8c688797ba23b9ab50cef404eca4d004a948941b6ee533ead0ff3bf52012528c md5: be60c4e8efa55fddc17b4131aa47acbd @@ -4598,22 +9948,57 @@ packages: purls: [] size: 107439 timestamp: 1727963788936 -- conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.23.0-py310h29418f3_3.conda - sha256: 1282801d99392c8e674151633c3120c12452a4ca6c2141b90b164c6b8a7f1724 - md5: c7ced46235127f2ec7ea29b95840c343 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py310h139afa4_1.conda + sha256: b0103e8bb639dbc6b9de8ef9a18a06b403b687a33dec83c25bd003190942259a + md5: 3741aefc198dfed2e3c9adc79d706bb7 depends: + - python - cffi >=1.11 - - python >=3.10,<3.11.0a0 + - zstd >=1.5.7,<1.5.8.0a0 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 - python_abi 3.10.* *_cp310 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/zstandard?source=hash-mapping + size: 455614 + timestamp: 1762512676430 +- conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py310h1637853_1.conda + sha256: db2a40dbe124b275fb0b8fdfd6e3b377963849897ab2b4d7696354040c52570b + md5: 1d261480977c268b3b209b7deaca0dd7 + depends: + - python + - cffi >=1.11 + - zstd >=1.5.7,<1.5.8.0a0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.10.* *_cp310 + - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/zstandard?source=hash-mapping - size: 333571 - timestamp: 1756075855434 + size: 364167 + timestamp: 1762512706699 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + sha256: a4166e3d8ff4e35932510aaff7aa90772f84b4d07e9f6f83c614cba7ceefe0eb + md5: 6432cb5d4ac0046c3ac0a8a0f95842f9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 567578 + timestamp: 1742433379869 - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-hbeecb71_2.conda sha256: bc64864377d809b904e877a98d0584f43836c9f2ef27d3d2a1421fa6eae7ca04 md5: 21f56217d6125fb30c3c3f10c786d751 diff --git a/pixi.toml b/pixi.toml index be4c9b2..36d4ee7 100644 --- a/pixi.toml +++ b/pixi.toml @@ -2,22 +2,29 @@ authors = ["AXIBA "] channels = ["conda-forge", "pytorch", "nvidia"] name = "OpenCDA" -platforms = ["win-64"] +platforms = ["win-64","linux-64"] version = "0.1.0-alpha" [system-requirements] cuda = "12.8" +[activation.env] +SUMO_HOME = "D:\\IDKs\\Eclipse\\Sumo" + [tasks] start = "python opencda.py" +# TensorBoard for training visualization +fix-tensorboard = "python scripts/fix_tensorboard_open3d.py" # Fix Open3D plugin issue +tensorboard = "tensorboard --logdir=runs" +tb = "tensorboard --logdir=runs" # Short alias + # MARL tests marl-intersection-build = "python test/marl/test_intersection_build.py" # MARL integrated with opencda.py -marl-dev = "python opencda.py -t intersection --marl" -marl-quick-test = "python opencda.py -t intersection_rule_based --marl" -marl-quick-test-gui = "python opencda.py -t intersection_rule_based --marl --gui" +marl-quick-test = "python opencda.py -t rule_based --marl" +marl-quick-test-gui = "python opencda.py -t rule_based --marl --gui" marl-eval-agents = "python opencda_marl/envs/cross_agent_evaluator.py" @@ -35,9 +42,10 @@ mkdocs-material = ">=9.5.0,<10" mkdocs-mermaid2-plugin = ">=1.2.1, <2" mkdocs-git-revision-date-localized-plugin = ">=1.4.7, <2" pymdown-extensions = ">=10.12.0,<11" +click = "*" [feature.docs.tasks] -docs-serve = "mkdocs serve" +docs-serve = "mkdocs serve --livereload" docs-build = "mkdocs build" docs-deploy = "mkdocs gh-deploy" @@ -70,10 +78,21 @@ loguru = ">=0.7.3,<0.8" pyyaml = ">=6.0.2,<7" h5py = ">=3.14.0,<4" pyside6 = ">=6.9.1,<7" +typing_extensions = ">=4.15.0,<5" +tensorboard = ">=2.20.0,<3" +pyclean = ">=3.4.0,<4" [pypi-dependencies] +gitpython = ">=3.1.30" +sumolib = ">=1.24.0, <2" + + +[target.win-64.pypi-dependencies] torch = { index = "https://download.pytorch.org/whl/nightly/cu128" } torchvision = { index = "https://download.pytorch.org/whl/nightly/cu128" } carla = { path = "./dependencies/whl/carla-0.9.15-cp310-cp310-win_amd64.whl" } -gitpython = ">=3.1.30" -sumolib = ">=1.24.0, <2" + +[target.linux-64.pypi-dependencies] +torch = { index = "https://download.pytorch.org/whl/cu130" } +torchvision = { index = "https://download.pytorch.org/whl/cu130" } +carla = { path = "./dependencies/whl/carla-0.9.15-cp310-cp310-manylinux_2_27_x86_64.whl" } diff --git a/recordings/lite_2min_SUMO.json b/recordings/lite_2min_SUMO.json new file mode 100644 index 0000000..f0c8682 --- /dev/null +++ b/recordings/lite_2min_SUMO.json @@ -0,0 +1,3367 @@ +{ + "version": "1.0", + "timestamp": "2025-11-17T16:56:04.837614", + "total_events": 80, + "events": [ + { + "event_id": "a8d8e4e6", + "vehicle_id": "west_W_2_14", + "flow_name": "west", + "spawn_step": 324, + "junction_id": 4, + "route_id": 11, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 55.44332830109583, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0196368575987034 + } + }, + { + "event_id": "26d1b636", + "vehicle_id": "north_N_2_14", + "flow_name": "north", + "spawn_step": 448, + "junction_id": 4, + "route_id": 1, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 45.93673136291808, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "758932d5", + "vehicle_id": "north_N_2_15", + "flow_name": "north", + "spawn_step": 454, + "junction_id": 4, + "route_id": 2, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 46.39233861462172, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "6bed4702", + "vehicle_id": "north_N_2_16", + "flow_name": "north", + "spawn_step": 468, + "junction_id": 4, + "route_id": 2, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 36.82483890580192, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "c31b40a7", + "vehicle_id": "west_W_1_7", + "flow_name": "west", + "spawn_step": 540, + "junction_id": 4, + "route_id": 9, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 34.77614034321774, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0961573416001196 + } + }, + { + "event_id": "019a22e7", + "vehicle_id": "east_E_0_0", + "flow_name": "east", + "spawn_step": 548, + "junction_id": 4, + "route_id": 7, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 43.14150115782684, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "887c04db", + "vehicle_id": "south_S_2_14", + "flow_name": "south", + "spawn_step": 553, + "junction_id": 4, + "route_id": 3, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 39.72367382085199, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.1462750865634108 + } + }, + { + "event_id": "e9b2a861", + "vehicle_id": "west_W_0_0", + "flow_name": "west", + "spawn_step": 559, + "junction_id": 4, + "route_id": 9, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 34.96371092097694, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.1076777741204638 + } + }, + { + "event_id": "02a45b9b", + "vehicle_id": "west_W_0_1", + "flow_name": "west", + "spawn_step": 599, + "junction_id": 4, + "route_id": 9, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 48.1418159602006, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.1352780728879746 + } + }, + { + "event_id": "0bf309f5", + "vehicle_id": "south_S_0_0", + "flow_name": "south", + "spawn_step": 659, + "junction_id": 4, + "route_id": 4, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 46.33731646993154, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.2029813684396262 + } + }, + { + "event_id": "ad74a600", + "vehicle_id": "south_S_1_7", + "flow_name": "south", + "spawn_step": 685, + "junction_id": 4, + "route_id": 3, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 40.2306321679057, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2163950044927496 + } + }, + { + "event_id": "0525f97e", + "vehicle_id": "west_W_1_8", + "flow_name": "west", + "spawn_step": 724, + "junction_id": 4, + "route_id": 11, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.496691679314594, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.2446982793622825 + } + }, + { + "event_id": "fb39388c", + "vehicle_id": "west_W_2_15", + "flow_name": "west", + "spawn_step": 726, + "junction_id": 4, + "route_id": 9, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 41.83989604142773, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.2458181767094105 + } + }, + { + "event_id": "adf6121e", + "vehicle_id": "north_N_1_7", + "flow_name": "north", + "spawn_step": 743, + "junction_id": 4, + "route_id": 2, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.16564627051049, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0799098203793236 + } + }, + { + "event_id": "56560b5f", + "vehicle_id": "south_S_0_1", + "flow_name": "south", + "spawn_step": 784, + "junction_id": 4, + "route_id": 5, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 45.25336112419851, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.2624965899047145 + } + }, + { + "event_id": "b4447faf", + "vehicle_id": "west_W_0_2", + "flow_name": "west", + "spawn_step": 785, + "junction_id": 4, + "route_id": 9, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 48.49641119702372, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.3056488373583237 + } + }, + { + "event_id": "a21a6bee", + "vehicle_id": "west_W_2_16", + "flow_name": "west", + "spawn_step": 805, + "junction_id": 4, + "route_id": 10, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 51.46870714124019, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.3262735965484092 + } + }, + { + "event_id": "862c1207", + "vehicle_id": "east_E_0_1", + "flow_name": "east", + "spawn_step": 836, + "junction_id": 4, + "route_id": 6, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 40.52920784491556, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "e0eba656", + "vehicle_id": "south_S_2_15", + "flow_name": "south", + "spawn_step": 850, + "junction_id": 4, + "route_id": 3, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 44.983416550269254, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.2846786261228145 + } + }, + { + "event_id": "de364cca", + "vehicle_id": "south_S_1_8", + "flow_name": "south", + "spawn_step": 895, + "junction_id": 4, + "route_id": 4, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 48.714639821505855, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.2945781681346553 + } + }, + { + "event_id": "e1cc06ed", + "vehicle_id": "west_W_0_3", + "flow_name": "west", + "spawn_step": 904, + "junction_id": 4, + "route_id": 11, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 37.5708651272578, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.419857316681775 + } + }, + { + "event_id": "9409af7d", + "vehicle_id": "west_W_2_17", + "flow_name": "west", + "spawn_step": 922, + "junction_id": 4, + "route_id": 9, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 40.44188350058675, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.434045988645119 + } + }, + { + "event_id": "0a89f36f", + "vehicle_id": "west_W_1_9", + "flow_name": "west", + "spawn_step": 928, + "junction_id": 4, + "route_id": 9, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 36.389285225251, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.4391637654913008 + } + }, + { + "event_id": "d3163a3b", + "vehicle_id": "south_S_0_2", + "flow_name": "south", + "spawn_step": 934, + "junction_id": 4, + "route_id": 3, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 39.245728466480855, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.2991415818759515 + } + }, + { + "event_id": "bad0fa54", + "vehicle_id": "west_W_1_10", + "flow_name": "west", + "spawn_step": 953, + "junction_id": 4, + "route_id": 11, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 35.219373068207226, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.4565152759501399 + } + }, + { + "event_id": "7cfaffa9", + "vehicle_id": "north_N_0_0", + "flow_name": "north", + "spawn_step": 958, + "junction_id": 4, + "route_id": 2, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 35.51931101382371, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.2553848109979093 + } + }, + { + "event_id": "fcd5df8d", + "vehicle_id": "south_S_0_3", + "flow_name": "south", + "spawn_step": 959, + "junction_id": 4, + "route_id": 4, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.06031457757519, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.2999987790807694 + } + }, + { + "event_id": "d3dc30e7", + "vehicle_id": "north_N_0_1", + "flow_name": "north", + "spawn_step": 962, + "junction_id": 4, + "route_id": 1, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 38.98014402826086, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.2592969709710011 + } + }, + { + "event_id": "dbf1897a", + "vehicle_id": "west_W_2_18", + "flow_name": "west", + "spawn_step": 963, + "junction_id": 4, + "route_id": 10, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 52.069261038240846, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.4629213163749228 + } + }, + { + "event_id": "2fc92f89", + "vehicle_id": "south_S_1_9", + "flow_name": "south", + "spawn_step": 988, + "junction_id": 4, + "route_id": 5, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 38.97768593386873, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.298934149202163 + } + }, + { + "event_id": "18a66dc6", + "vehicle_id": "south_S_2_16", + "flow_name": "south", + "spawn_step": 1019, + "junction_id": 4, + "route_id": 4, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 44.92707389153186, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.2953741967147718 + } + }, + { + "event_id": "928ce6ce", + "vehicle_id": "south_S_0_4", + "flow_name": "south", + "spawn_step": 1024, + "junction_id": 4, + "route_id": 5, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 46.09321061670506, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.2945913328877459 + } + }, + { + "event_id": "6fde8622", + "vehicle_id": "east_E_2_14", + "flow_name": "east", + "spawn_step": 1024, + "junction_id": 4, + "route_id": 7, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 44.21545928487429, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0511761083302982 + } + }, + { + "event_id": "c41ca2ae", + "vehicle_id": "north_N_2_17", + "flow_name": "north", + "spawn_step": 1026, + "junction_id": 4, + "route_id": 0, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 52.031597969632934, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.3168179927684225 + } + }, + { + "event_id": "226bda36", + "vehicle_id": "east_E_2_15", + "flow_name": "east", + "spawn_step": 1035, + "junction_id": 4, + "route_id": 8, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 41.353268455700324, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0566902529600897 + } + }, + { + "event_id": "148d183f", + "vehicle_id": "north_N_0_2", + "flow_name": "north", + "spawn_step": 1085, + "junction_id": 4, + "route_id": 2, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 46.437519451103334, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.361778735902034 + } + }, + { + "event_id": "82bf5ef7", + "vehicle_id": "east_E_1_7", + "flow_name": "east", + "spawn_step": 1089, + "junction_id": 4, + "route_id": 7, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 42.27490389595003, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0891258389739928 + } + }, + { + "event_id": "55cdce6f", + "vehicle_id": "west_W_1_11", + "flow_name": "west", + "spawn_step": 1102, + "junction_id": 4, + "route_id": 9, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 41.95762959223085, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.4985383249318498 + } + }, + { + "event_id": "e3c01284", + "vehicle_id": "east_E_0_2", + "flow_name": "east", + "spawn_step": 1122, + "junction_id": 4, + "route_id": 8, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 48.683809143992264, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.1143590121530165 + } + }, + { + "event_id": "db4964e5", + "vehicle_id": "west_W_1_12", + "flow_name": "west", + "spawn_step": 1148, + "junction_id": 4, + "route_id": 9, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 49.40298252109099, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.4867116551394957 + } + }, + { + "event_id": "27eb1582", + "vehicle_id": "west_W_0_4", + "flow_name": "west", + "spawn_step": 1153, + "junction_id": 4, + "route_id": 10, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 36.189798006178, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.4849382787226362 + } + }, + { + "event_id": "5210ce3b", + "vehicle_id": "north_N_0_3", + "flow_name": "north", + "spawn_step": 1172, + "junction_id": 4, + "route_id": 2, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 44.77480649388056, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.3976203841316248 + } + }, + { + "event_id": "cf4d341c", + "vehicle_id": "east_E_2_16", + "flow_name": "east", + "spawn_step": 1184, + "junction_id": 4, + "route_id": 6, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 42.806534632793024, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.1690832773227222 + } + }, + { + "event_id": "ca35d1d3", + "vehicle_id": "north_N_2_18", + "flow_name": "north", + "spawn_step": 1211, + "junction_id": 4, + "route_id": 0, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 41.13024272621802, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.399612168668537 + } + }, + { + "event_id": "873f8395", + "vehicle_id": "west_W_2_19", + "flow_name": "west", + "spawn_step": 1214, + "junction_id": 4, + "route_id": 10, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 41.314212392920375, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.4514816981189806 + } + }, + { + "event_id": "59ba2b7a", + "vehicle_id": "south_S_1_10", + "flow_name": "south", + "spawn_step": 1215, + "junction_id": 4, + "route_id": 3, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 47.21619061270389, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2258787983840755 + } + }, + { + "event_id": "58ab0c08", + "vehicle_id": "north_N_1_8", + "flow_name": "north", + "spawn_step": 1226, + "junction_id": 4, + "route_id": 0, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 46.634318173323386, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3978443112097771 + } + }, + { + "event_id": "4ef52007", + "vehicle_id": "west_W_1_13", + "flow_name": "west", + "spawn_step": 1230, + "junction_id": 4, + "route_id": 11, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 33.354475735863005, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.4396214289168217 + } + }, + { + "event_id": "bddf707c", + "vehicle_id": "east_E_2_17", + "flow_name": "east", + "spawn_step": 1261, + "junction_id": 4, + "route_id": 8, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 44.66898459296094, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.2460864258316386 + } + }, + { + "event_id": "e192fbb6", + "vehicle_id": "north_N_0_4", + "flow_name": "north", + "spawn_step": 1281, + "junction_id": 4, + "route_id": 0, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 36.25371240706023, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.3801895549100356 + } + }, + { + "event_id": "2d70d951", + "vehicle_id": "north_N_0_5", + "flow_name": "north", + "spawn_step": 1287, + "junction_id": 4, + "route_id": 0, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 44.36317395261946, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.3772551451294042 + } + }, + { + "event_id": "0432c36a", + "vehicle_id": "south_S_2_17", + "flow_name": "south", + "spawn_step": 1332, + "junction_id": 4, + "route_id": 4, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.516569416121776, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.1641347225100922 + } + }, + { + "event_id": "c2363419", + "vehicle_id": "east_E_0_3", + "flow_name": "east", + "spawn_step": 1338, + "junction_id": 4, + "route_id": 6, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 46.824802839772765, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.312377094631297 + } + }, + { + "event_id": "b01e232f", + "vehicle_id": "south_S_1_11", + "flow_name": "south", + "spawn_step": 1344, + "junction_id": 4, + "route_id": 5, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 47.57195007880773, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.1578031087822365 + } + }, + { + "event_id": "8ad25e8d", + "vehicle_id": "south_S_1_12", + "flow_name": "south", + "spawn_step": 1348, + "junction_id": 4, + "route_id": 4, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 42.46481672028954, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.1558142703262704 + } + }, + { + "event_id": "bd1c840b", + "vehicle_id": "east_E_2_18", + "flow_name": "east", + "spawn_step": 1359, + "junction_id": 4, + "route_id": 6, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 45.34303793386076, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.3259040018808812 + } + }, + { + "event_id": "f48bb3b6", + "vehicle_id": "east_E_1_8", + "flow_name": "east", + "spawn_step": 1361, + "junction_id": 4, + "route_id": 6, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 39.92293234768898, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.326610114933343 + } + }, + { + "event_id": "933ece93", + "vehicle_id": "east_E_0_4", + "flow_name": "east", + "spawn_step": 1365, + "junction_id": 4, + "route_id": 7, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 40.49822722319773, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.3289093710679385 + } + }, + { + "event_id": "d9c2c5ff", + "vehicle_id": "north_N_1_9", + "flow_name": "north", + "spawn_step": 1387, + "junction_id": 4, + "route_id": 1, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 37.61703285930619, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.304546347887649 + } + }, + { + "event_id": "d4366ff0", + "vehicle_id": "east_E_1_9", + "flow_name": "east", + "spawn_step": 1410, + "junction_id": 4, + "route_id": 7, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 47.59342392593538, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3465257703880154 + } + }, + { + "event_id": "e5bd130c", + "vehicle_id": "east_E_0_5", + "flow_name": "east", + "spawn_step": 1416, + "junction_id": 4, + "route_id": 6, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 44.294486940954556, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.347787510565659 + } + }, + { + "event_id": "f30f3447", + "vehicle_id": "south_S_2_18", + "flow_name": "south", + "spawn_step": 1467, + "junction_id": 4, + "route_id": 3, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 46.98660852335177, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.098303705551172 + } + }, + { + "event_id": "3d160d4a", + "vehicle_id": "north_N_1_10", + "flow_name": "north", + "spawn_step": 1521, + "junction_id": 4, + "route_id": 1, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 36.98972930647836, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.1804370178470296 + } + }, + { + "event_id": "dadcacd4", + "vehicle_id": "west_W_0_5", + "flow_name": "west", + "spawn_step": 1521, + "junction_id": 4, + "route_id": 9, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 41.031645498363986, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.1653935070938108 + } + }, + { + "event_id": "24e0a2cb", + "vehicle_id": "east_E_0_6", + "flow_name": "east", + "spawn_step": 1567, + "junction_id": 4, + "route_id": 7, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 40.2946734762029, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.2920330580626123 + } + }, + { + "event_id": "5caeebff", + "vehicle_id": "north_N_1_11", + "flow_name": "north", + "spawn_step": 1571, + "junction_id": 4, + "route_id": 0, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.54977604469654, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.1375717382917188 + } + }, + { + "event_id": "0f07a2b5", + "vehicle_id": "north_N_0_6", + "flow_name": "north", + "spawn_step": 1667, + "junction_id": 4, + "route_id": 2, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 39.67100531067286, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0742910321272645 + } + }, + { + "event_id": "e59e7f5a", + "vehicle_id": "east_E_1_10", + "flow_name": "east", + "spawn_step": 1681, + "junction_id": 4, + "route_id": 8, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 40.894512281905804, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.1826914753403817 + } + }, + { + "event_id": "98212761", + "vehicle_id": "north_N_2_19", + "flow_name": "north", + "spawn_step": 1688, + "junction_id": 4, + "route_id": 1, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 38.68036739675803, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0635552275986675 + } + }, + { + "event_id": "341a50f0", + "vehicle_id": "north_N_1_12", + "flow_name": "north", + "spawn_step": 1732, + "junction_id": 4, + "route_id": 2, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.30083713787895, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0450186808002646 + } + }, + { + "event_id": "080713fe", + "vehicle_id": "east_E_1_11", + "flow_name": "east", + "spawn_step": 1737, + "junction_id": 4, + "route_id": 7, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 42.324489229029, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.130818179817222 + } + }, + { + "event_id": "5960b33e", + "vehicle_id": "east_E_2_19", + "flow_name": "east", + "spawn_step": 1747, + "junction_id": 4, + "route_id": 6, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 40.60360371954598, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.1223645831357034 + } + }, + { + "event_id": "4c0d4980", + "vehicle_id": "south_S_0_5", + "flow_name": "south", + "spawn_step": 1813, + "junction_id": 4, + "route_id": 3, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 49.28142112306412, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0127499734272318 + } + }, + { + "event_id": "ba2d0fdc", + "vehicle_id": "north_N_1_13", + "flow_name": "north", + "spawn_step": 1818, + "junction_id": 4, + "route_id": 2, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 45.55659367437995, + "spawn_transform": { + "location": { + "x": -8.75, + "y": 25.569999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.020921701300026 + } + }, + { + "event_id": "c06c8ff8", + "vehicle_id": "west_W_0_6", + "flow_name": "west", + "spawn_step": 1832, + "junction_id": 4, + "route_id": 10, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 53.07941418974629, + "spawn_transform": { + "location": { + "x": -26.0, + "y": -9.180000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0201015288938429 + } + }, + { + "event_id": "0dd03e12", + "vehicle_id": "south_S_2_19", + "flow_name": "south", + "spawn_step": 1902, + "junction_id": 4, + "route_id": 5, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 48.595253288184104, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0063401369920335 + } + }, + { + "event_id": "dfe7cb1b", + "vehicle_id": "south_S_0_6", + "flow_name": "south", + "spawn_step": 1945, + "junction_id": 4, + "route_id": 4, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 41.86450182567199, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "05bc1beb", + "vehicle_id": "east_E_1_12", + "flow_name": "east", + "spawn_step": 2031, + "junction_id": 4, + "route_id": 8, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 49.144079432771925, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0071547877890117 + } + }, + { + "event_id": "91fafbc6", + "vehicle_id": "east_E_1_13", + "flow_name": "east", + "spawn_step": 2101, + "junction_id": 4, + "route_id": 8, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 46.91654075537209, + "spawn_transform": { + "location": { + "x": 26.0, + "y": 8.319999999999993, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "757f8b53", + "vehicle_id": "south_S_1_13", + "flow_name": "south", + "spawn_step": 2155, + "junction_id": 4, + "route_id": 3, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 39.47727706434622, + "spawn_transform": { + "location": { + "x": 8.75, + "y": -26.430000000000007, + "z": 0.3 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": -16.430000000000007, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0 + } + } + ] +} \ No newline at end of file diff --git a/recordings/quick_test.json b/recordings/quick_test.json deleted file mode 100644 index 8675c7c..0000000 --- a/recordings/quick_test.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "version": "1.0", - "timestamp": "2025-08-30T14:14:42.939405", - "total_events": 3, - "events": [ - { - "event_id": "c6bd39ad", - "vehicle_id": "west_W_0_0", - "flow_name": "west", - "spawn_step": 1, - "junction_id": 4, - "route_id": 188, - "lane_id": 0, - "blueprint_id": "vehicle.bmw.grandtourer", - "target_speed": 49.709261043828576, - "spawn_transform": { - "location": { - "x": -56.0, - "y": 8.75, - "z": 0.30000001192092896 - }, - "rotation": { - "pitch": 0.0, - "yaw": -0.0, - "roll": 0.0 - } - }, - "destination_transform": { - "location": { - "x": 56.0, - "y": 5.25, - "z": 0.0 - }, - "rotation": { - "pitch": 0.0, - "yaw": -0.0, - "roll": 0.0 - } - }, - "metadata": { - "route_type": "straight", - "entry_direction": "west", - "exit_direction": "east", - "lane_id": 0, - "peak_density": 1.0164869684168476 - } - }, - { - "event_id": "8e5faf35", - "vehicle_id": "west_W_1_7", - "flow_name": "west", - "spawn_step": 15, - "junction_id": 4, - "route_id": 146, - "lane_id": 1, - "blueprint_id": "vehicle.citroen.c3", - "target_speed": 35.247441595338806, - "spawn_transform": { - "location": { - "x": -56.0, - "y": 5.25, - "z": 0.30000001192092896 - }, - "rotation": { - "pitch": 0.0, - "yaw": -0.0, - "roll": 0.0 - } - }, - "destination_transform": { - "location": { - "x": -5.25, - "y": 56.0, - "z": 0.0 - }, - "rotation": { - "pitch": 0.0, - "yaw": 90.0, - "roll": 0.0 - } - }, - "metadata": { - "route_type": "right", - "entry_direction": "west", - "exit_direction": "north", - "lane_id": 1, - "peak_density": 1.0274015339210927 - } - }, - { - "event_id": "cc115611", - "vehicle_id": "east_E_0_0", - "flow_name": "east", - "spawn_step": 20, - "junction_id": 4, - "route_id": 120, - "lane_id": 0, - "blueprint_id": "vehicle.lincoln.mkz_2017", - "target_speed": 49.46509242216087, - "spawn_transform": { - "location": { - "x": 56.0, - "y": -8.75, - "z": 0.30000001192092896 - }, - "rotation": { - "pitch": 360.0, - "yaw": 180.0, - "roll": 0.0 - } - }, - "destination_transform": { - "location": { - "x": 1.75, - "y": -56.0, - "z": 0.0 - }, - "rotation": { - "pitch": 360.0, - "yaw": 270.0, - "roll": 0.0 - } - }, - "metadata": { - "route_type": "right", - "entry_direction": "east", - "exit_direction": "south", - "lane_id": 0, - "peak_density": 1.0 - } - } - ] -} \ No newline at end of file diff --git a/recordings/traffic_100vph.json b/recordings/traffic_100vph.json new file mode 100644 index 0000000..e146419 --- /dev/null +++ b/recordings/traffic_100vph.json @@ -0,0 +1,1687 @@ +{ + "version": "1.0", + "timestamp": "2025-12-10T14:17:27.254796", + "total_events": 40, + "events": [ + { + "event_id": "a3a97d09", + "vehicle_id": "west_W_0_0", + "flow_name": "west", + "spawn_step": 265, + "junction_id": 4, + "route_id": 155, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 53.909463110220386, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0116372293804246 + } + }, + { + "event_id": "a276f82d", + "vehicle_id": "west_W_0_1", + "flow_name": "west", + "spawn_step": 273, + "junction_id": 4, + "route_id": 148, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 63.39100405644804, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0125424462603698 + } + }, + { + "event_id": "20b51aeb", + "vehicle_id": "south_S_0_0", + "flow_name": "south", + "spawn_step": 357, + "junction_id": 4, + "route_id": 81, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 53.398218918061595, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0620781816648643 + } + }, + { + "event_id": "c245aa4e", + "vehicle_id": "south_S_0_1", + "flow_name": "south", + "spawn_step": 399, + "junction_id": 4, + "route_id": 61, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 58.758564081592866, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.07655755829355 + } + }, + { + "event_id": "29d5ef64", + "vehicle_id": "north_N_0_0", + "flow_name": "north", + "spawn_step": 417, + "junction_id": 4, + "route_id": 40, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 62.516725659794275, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "b67d6b6b", + "vehicle_id": "west_W_2_7", + "flow_name": "west", + "spawn_step": 452, + "junction_id": 4, + "route_id": 168, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 66.37096579403338, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.053691783563124 + } + }, + { + "event_id": "2d5506ad", + "vehicle_id": "north_N_1_4", + "flow_name": "north", + "spawn_step": 478, + "junction_id": 4, + "route_id": 31, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 46.46108995705969, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "6b03689a", + "vehicle_id": "south_S_1_4", + "flow_name": "south", + "spawn_step": 495, + "junction_id": 4, + "route_id": 81, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 48.174257401801235, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.1174055608515496 + } + }, + { + "event_id": "7789eb1b", + "vehicle_id": "south_S_2_7", + "flow_name": "south", + "spawn_step": 497, + "junction_id": 4, + "route_id": 62, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 47.35423872384056, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.118349497709717 + } + }, + { + "event_id": "aa866beb", + "vehicle_id": "north_N_0_1", + "flow_name": "north", + "spawn_step": 550, + "junction_id": 4, + "route_id": 32, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 48.30162249394339, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0153840079427194 + } + }, + { + "event_id": "05ebcd26", + "vehicle_id": "south_S_2_8", + "flow_name": "south", + "spawn_step": 684, + "junction_id": 4, + "route_id": 88, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 49.43817898703039, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.2155973669172353 + } + }, + { + "event_id": "f10ef3c6", + "vehicle_id": "north_N_0_2", + "flow_name": "north", + "spawn_step": 751, + "junction_id": 4, + "route_id": 23, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 55.39140092803736, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0850132201185543 + } + }, + { + "event_id": "891c0689", + "vehicle_id": "south_S_0_2", + "flow_name": "south", + "spawn_step": 754, + "junction_id": 4, + "route_id": 92, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 57.051235338780614, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.249928437467946 + } + }, + { + "event_id": "f15eae73", + "vehicle_id": "south_S_2_9", + "flow_name": "south", + "spawn_step": 798, + "junction_id": 4, + "route_id": 50, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 46.636316865145076, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.2677070665460006 + } + }, + { + "event_id": "5e9640a3", + "vehicle_id": "west_W_1_4", + "flow_name": "west", + "spawn_step": 828, + "junction_id": 4, + "route_id": 159, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.597356904579556, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.3488757871658215 + } + }, + { + "event_id": "2a96abf3", + "vehicle_id": "east_E_1_4", + "flow_name": "east", + "spawn_step": 829, + "junction_id": 4, + "route_id": 102, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 53.39727323248509, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "939ca300", + "vehicle_id": "south_S_1_5", + "flow_name": "south", + "spawn_step": 868, + "junction_id": 4, + "route_id": 62, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 50.813984174617794, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2894082869005035 + } + }, + { + "event_id": "4b32b6b8", + "vehicle_id": "west_W_2_8", + "flow_name": "west", + "spawn_step": 903, + "junction_id": 4, + "route_id": 173, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 50.66781379300191, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.4188310782343692 + } + }, + { + "event_id": "6ac9fdb6", + "vehicle_id": "south_S_0_3", + "flow_name": "south", + "spawn_step": 996, + "junction_id": 4, + "route_id": 73, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 52.36867810671207, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.2982611155670485 + } + }, + { + "event_id": "aa6fc834", + "vehicle_id": "west_W_0_2", + "flow_name": "west", + "spawn_step": 1047, + "junction_id": 4, + "route_id": 179, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 40.52219130051749, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.4969346082900317 + } + }, + { + "event_id": "52d626e6", + "vehicle_id": "east_E_0_0", + "flow_name": "east", + "spawn_step": 1059, + "junction_id": 4, + "route_id": 111, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 56.46104890866898, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0699464222641353 + } + }, + { + "event_id": "ca6a44c5", + "vehicle_id": "west_W_2_9", + "flow_name": "west", + "spawn_step": 1107, + "junction_id": 4, + "route_id": 190, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 56.49141084656406, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.4978936057039423 + } + }, + { + "event_id": "a9724a8a", + "vehicle_id": "west_W_0_3", + "flow_name": "west", + "spawn_step": 1111, + "junction_id": 4, + "route_id": 178, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 64.52181320713608, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.497159539775796 + } + }, + { + "event_id": "d592a876", + "vehicle_id": "east_E_2_7", + "flow_name": "east", + "spawn_step": 1131, + "junction_id": 4, + "route_id": 142, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 57.595531069158596, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.121939463948122 + } + }, + { + "event_id": "1569fd51", + "vehicle_id": "east_E_0_1", + "flow_name": "east", + "spawn_step": 1165, + "junction_id": 4, + "route_id": 109, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 50.18284936308989, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.1516189673586035 + } + }, + { + "event_id": "bfccf4b1", + "vehicle_id": "north_N_2_7", + "flow_name": "north", + "spawn_step": 1188, + "junction_id": 4, + "route_id": 5, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 59.47703007118704, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.3995772634735588 + } + }, + { + "event_id": "163171d7", + "vehicle_id": "south_S_1_6", + "flow_name": "south", + "spawn_step": 1195, + "junction_id": 4, + "route_id": 85, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 49.353727007364334, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.2360513193967473 + } + }, + { + "event_id": "7044bd57", + "vehicle_id": "north_N_1_5", + "flow_name": "north", + "spawn_step": 1215, + "junction_id": 4, + "route_id": 23, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 47.30076348658361, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.3992833693741031 + } + }, + { + "event_id": "6981c446", + "vehicle_id": "east_E_0_2", + "flow_name": "east", + "spawn_step": 1265, + "junction_id": 4, + "route_id": 132, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 53.33249756261689, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.249890162300061 + } + }, + { + "event_id": "90cbd445", + "vehicle_id": "east_E_1_5", + "flow_name": "east", + "spawn_step": 1265, + "junction_id": 4, + "route_id": 122, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 57.52788428274796, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.249875401861077 + } + }, + { + "event_id": "30ea5398", + "vehicle_id": "east_E_2_8", + "flow_name": "east", + "spawn_step": 1313, + "junction_id": 4, + "route_id": 102, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.094244636133354, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.2932221920027411 + } + }, + { + "event_id": "8c4a0d2e", + "vehicle_id": "west_W_1_5", + "flow_name": "west", + "spawn_step": 1355, + "junction_id": 4, + "route_id": 174, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 62.91637268962284, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3247088720581826 + } + }, + { + "event_id": "b71b0926", + "vehicle_id": "north_N_2_8", + "flow_name": "north", + "spawn_step": 1452, + "junction_id": 4, + "route_id": 34, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 45.679356367897434, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.2445841613681883 + } + }, + { + "event_id": "e473a91d", + "vehicle_id": "east_E_2_9", + "flow_name": "east", + "spawn_step": 1577, + "junction_id": 4, + "route_id": 130, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 49.59440166926966, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.283610252216953 + } + }, + { + "event_id": "2fecba5d", + "vehicle_id": "north_N_2_9", + "flow_name": "north", + "spawn_step": 1611, + "junction_id": 4, + "route_id": 30, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 47.11741160327888, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.108198881618748 + } + }, + { + "event_id": "b09f1ee4", + "vehicle_id": "west_W_1_6", + "flow_name": "west", + "spawn_step": 1617, + "junction_id": 4, + "route_id": 147, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 51.169857865027595, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.09713292122046 + } + }, + { + "event_id": "3a4bde5b", + "vehicle_id": "east_E_0_3", + "flow_name": "east", + "spawn_step": 1763, + "junction_id": 4, + "route_id": 126, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 51.02879525873408, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.1096461250577732 + } + }, + { + "event_id": "bca8d397", + "vehicle_id": "east_E_1_6", + "flow_name": "east", + "spawn_step": 1818, + "junction_id": 4, + "route_id": 99, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 49.807982519337784, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0715128987241278 + } + }, + { + "event_id": "feb41000", + "vehicle_id": "north_N_1_6", + "flow_name": "north", + "spawn_step": 1938, + "junction_id": 4, + "route_id": 15, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 64.83288779655587, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "5469db5a", + "vehicle_id": "north_N_0_3", + "flow_name": "north", + "spawn_step": 2125, + "junction_id": 4, + "route_id": 33, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 57.176780858125035, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0 + } + } + ] +} \ No newline at end of file diff --git a/recordings/traffic_200vph.json b/recordings/traffic_200vph.json new file mode 100644 index 0000000..936cb91 --- /dev/null +++ b/recordings/traffic_200vph.json @@ -0,0 +1,3367 @@ +{ + "version": "1.0", + "timestamp": "2025-12-10T14:19:31.140202", + "total_events": 80, + "events": [ + { + "event_id": "6a7af890", + "vehicle_id": "south_S_0_0", + "flow_name": "south", + "spawn_step": 260, + "junction_id": 4, + "route_id": 51, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 57.01085613293973, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0358008853094542 + } + }, + { + "event_id": "7d852198", + "vehicle_id": "south_S_2_14", + "flow_name": "south", + "spawn_step": 275, + "junction_id": 4, + "route_id": 95, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 59.42499318314096, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0392870348884982 + } + }, + { + "event_id": "b9bc5804", + "vehicle_id": "west_W_0_0", + "flow_name": "west", + "spawn_step": 277, + "junction_id": 4, + "route_id": 163, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 46.96873808219043, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0130311973258161 + } + }, + { + "event_id": "1cc9de8c", + "vehicle_id": "north_N_0_0", + "flow_name": "north", + "spawn_step": 278, + "junction_id": 4, + "route_id": 36, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 56.73621926515864, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "ffa4ef0e", + "vehicle_id": "south_S_2_15", + "flow_name": "south", + "spawn_step": 300, + "junction_id": 4, + "route_id": 74, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.92026171805119, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0453611111385726 + } + }, + { + "event_id": "a8c8fd6c", + "vehicle_id": "west_W_1_7", + "flow_name": "west", + "spawn_step": 403, + "junction_id": 4, + "route_id": 159, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 47.25020120003463, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0374079747611877 + } + }, + { + "event_id": "aab5806c", + "vehicle_id": "south_S_0_1", + "flow_name": "south", + "spawn_step": 425, + "junction_id": 4, + "route_id": 65, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 46.40260024960242, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.086715785976357 + } + }, + { + "event_id": "a5e053bb", + "vehicle_id": "north_N_2_14", + "flow_name": "north", + "spawn_step": 483, + "junction_id": 4, + "route_id": 35, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 64.44938922077334, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0076348357524796 + } + }, + { + "event_id": "b4c7e9ab", + "vehicle_id": "north_N_2_15", + "flow_name": "north", + "spawn_step": 511, + "junction_id": 4, + "route_id": 6, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 49.76672833678659, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0103419802277622 + } + }, + { + "event_id": "751d8988", + "vehicle_id": "north_N_0_1", + "flow_name": "north", + "spawn_step": 525, + "junction_id": 4, + "route_id": 2, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 45.749125427160735, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0119006916854263 + } + }, + { + "event_id": "544ca031", + "vehicle_id": "south_S_1_7", + "flow_name": "south", + "spawn_step": 534, + "junction_id": 4, + "route_id": 75, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 46.791147703676955, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.1368098748038309 + } + }, + { + "event_id": "498f37e7", + "vehicle_id": "south_S_1_8", + "flow_name": "south", + "spawn_step": 600, + "junction_id": 4, + "route_id": 71, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 60.52733099277016, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.1713034841246253 + } + }, + { + "event_id": "1141edaf", + "vehicle_id": "west_W_1_8", + "flow_name": "west", + "spawn_step": 610, + "junction_id": 4, + "route_id": 156, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 41.32158676972119, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.1431505447228092 + } + }, + { + "event_id": "b9bc919a", + "vehicle_id": "south_S_0_2", + "flow_name": "south", + "spawn_step": 676, + "junction_id": 4, + "route_id": 56, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 48.36996773249449, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.211699187269431 + } + }, + { + "event_id": "1cb4e62a", + "vehicle_id": "south_S_0_3", + "flow_name": "south", + "spawn_step": 682, + "junction_id": 4, + "route_id": 76, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 57.13961885680058, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.2147317318445483 + } + }, + { + "event_id": "051b570b", + "vehicle_id": "west_W_1_9", + "flow_name": "west", + "spawn_step": 696, + "junction_id": 4, + "route_id": 159, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 56.79789098667068, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2173858986827424 + } + }, + { + "event_id": "6daeda44", + "vehicle_id": "west_W_2_14", + "flow_name": "west", + "spawn_step": 723, + "junction_id": 4, + "route_id": 144, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 61.1320775527069, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.2433079001681158 + } + }, + { + "event_id": "610f22fc", + "vehicle_id": "east_E_1_7", + "flow_name": "east", + "spawn_step": 725, + "junction_id": 4, + "route_id": 116, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 56.16829738636758, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "b4dfb11e", + "vehicle_id": "west_W_1_10", + "flow_name": "west", + "spawn_step": 741, + "junction_id": 4, + "route_id": 148, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 51.26978576695116, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.261617697484792 + } + }, + { + "event_id": "2556bbd4", + "vehicle_id": "south_S_1_9", + "flow_name": "south", + "spawn_step": 766, + "junction_id": 4, + "route_id": 61, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 56.28240452026371, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2548717885934575 + } + }, + { + "event_id": "076f1cb0", + "vehicle_id": "west_W_2_15", + "flow_name": "west", + "spawn_step": 782, + "junction_id": 4, + "route_id": 158, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 61.44777045301416, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.3022662998335082 + } + }, + { + "event_id": "4a0c7db7", + "vehicle_id": "east_E_2_14", + "flow_name": "east", + "spawn_step": 801, + "junction_id": 4, + "route_id": 115, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 52.90213128429519, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "a4d389c8", + "vehicle_id": "north_N_0_2", + "flow_name": "north", + "spawn_step": 805, + "junction_id": 4, + "route_id": 42, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 55.756597844673514, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.1202081283177745 + } + }, + { + "event_id": "df4d7ec9", + "vehicle_id": "west_W_0_1", + "flow_name": "west", + "spawn_step": 813, + "junction_id": 4, + "route_id": 181, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 57.273921237336225, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.3342339571437971 + } + }, + { + "event_id": "c23ae390", + "vehicle_id": "east_E_0_0", + "flow_name": "east", + "spawn_step": 822, + "junction_id": 4, + "route_id": 108, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 53.11088146317477, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "4a488fa2", + "vehicle_id": "north_N_2_16", + "flow_name": "north", + "spawn_step": 862, + "junction_id": 4, + "route_id": 6, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 46.63407938031081, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.1657058316732885 + } + }, + { + "event_id": "a22a10cc", + "vehicle_id": "south_S_2_16", + "flow_name": "south", + "spawn_step": 869, + "junction_id": 4, + "route_id": 92, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 51.52542702189127, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.2895694063631302 + } + }, + { + "event_id": "ec161d70", + "vehicle_id": "north_N_1_7", + "flow_name": "north", + "spawn_step": 916, + "junction_id": 4, + "route_id": 4, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 52.920413662097516, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.2148333619325484 + } + }, + { + "event_id": "20aa0f2c", + "vehicle_id": "south_S_0_4", + "flow_name": "south", + "spawn_step": 918, + "junction_id": 4, + "route_id": 83, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 47.14869805201294, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.2978071012147927 + } + }, + { + "event_id": "427d6c1d", + "vehicle_id": "south_S_1_10", + "flow_name": "south", + "spawn_step": 922, + "junction_id": 4, + "route_id": 70, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 59.50911017716601, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.2982010759393443 + } + }, + { + "event_id": "accd3439", + "vehicle_id": "west_W_1_11", + "flow_name": "west", + "spawn_step": 938, + "junction_id": 4, + "route_id": 184, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 44.51731490260321, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.4463067345168368 + } + }, + { + "event_id": "1d18f7c3", + "vehicle_id": "south_S_0_5", + "flow_name": "south", + "spawn_step": 960, + "junction_id": 4, + "route_id": 92, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 54.07967362361333, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.2999998125852974 + } + }, + { + "event_id": "cf98ec97", + "vehicle_id": "east_E_2_15", + "flow_name": "east", + "spawn_step": 962, + "junction_id": 4, + "route_id": 122, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 53.77727182409597, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0276548076201255 + } + }, + { + "event_id": "8ef6d522", + "vehicle_id": "west_W_2_16", + "flow_name": "west", + "spawn_step": 973, + "junction_id": 4, + "route_id": 188, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 51.49271645593808, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.4687458878889317 + } + }, + { + "event_id": "c01cc8ac", + "vehicle_id": "south_S_0_6", + "flow_name": "south", + "spawn_step": 975, + "junction_id": 4, + "route_id": 71, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 54.19238001281412, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.2996928688264842 + } + }, + { + "event_id": "2ec0d9bc", + "vehicle_id": "east_E_1_8", + "flow_name": "east", + "spawn_step": 993, + "junction_id": 4, + "route_id": 128, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 53.53209453913702, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0381589326089822 + } + }, + { + "event_id": "8cafa6cc", + "vehicle_id": "north_N_1_8", + "flow_name": "north", + "spawn_step": 1022, + "junction_id": 4, + "route_id": 36, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 44.18147273902801, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.3135734911344945 + } + }, + { + "event_id": "b087bfaf", + "vehicle_id": "north_N_2_17", + "flow_name": "north", + "spawn_step": 1032, + "junction_id": 4, + "route_id": 18, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 47.06580618063328, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.32241867496682 + } + }, + { + "event_id": "a280fbae", + "vehicle_id": "south_S_2_17", + "flow_name": "south", + "spawn_step": 1048, + "junction_id": 4, + "route_id": 50, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 48.957662997806224, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.2900612469048218 + } + }, + { + "event_id": "fb5b0859", + "vehicle_id": "west_W_0_2", + "flow_name": "west", + "spawn_step": 1071, + "junction_id": 4, + "route_id": 173, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 50.73992396299987, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.4998129156543551 + } + }, + { + "event_id": "4f2db242", + "vehicle_id": "north_N_1_9", + "flow_name": "north", + "spawn_step": 1074, + "junction_id": 4, + "route_id": 13, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 48.07027262516007, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3539996822157838 + } + }, + { + "event_id": "a7783159", + "vehicle_id": "north_N_1_10", + "flow_name": "north", + "spawn_step": 1090, + "junction_id": 4, + "route_id": 22, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 61.03558352741705, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.3644171191180636 + } + }, + { + "event_id": "e96192f7", + "vehicle_id": "south_S_1_11", + "flow_name": "south", + "spawn_step": 1103, + "junction_id": 4, + "route_id": 74, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 57.49514965354464, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.274243942932827 + } + }, + { + "event_id": "a4811ad7", + "vehicle_id": "west_W_2_17", + "flow_name": "west", + "spawn_step": 1124, + "junction_id": 4, + "route_id": 179, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 63.19384908268273, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.4943675217463692 + } + }, + { + "event_id": "deaf374c", + "vehicle_id": "east_E_2_16", + "flow_name": "east", + "spawn_step": 1126, + "junction_id": 4, + "route_id": 114, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 50.30320129384218, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.117281444283602 + } + }, + { + "event_id": "f36fe610", + "vehicle_id": "west_W_2_18", + "flow_name": "west", + "spawn_step": 1158, + "junction_id": 4, + "route_id": 151, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 42.748481427637614, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.4827464888123245 + } + }, + { + "event_id": "9a2a0aae", + "vehicle_id": "east_E_1_9", + "flow_name": "east", + "spawn_step": 1159, + "junction_id": 4, + "route_id": 132, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 51.5376788529358, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.1461262438844935 + } + }, + { + "event_id": "b0e0d812", + "vehicle_id": "east_E_0_1", + "flow_name": "east", + "spawn_step": 1216, + "junction_id": 4, + "route_id": 100, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 52.290893403082755, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.2008361413815867 + } + }, + { + "event_id": "f7035c9a", + "vehicle_id": "east_E_2_17", + "flow_name": "east", + "spawn_step": 1238, + "junction_id": 4, + "route_id": 122, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 55.67976056096545, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.2231300682334763 + } + }, + { + "event_id": "93569821", + "vehicle_id": "north_N_1_11", + "flow_name": "north", + "spawn_step": 1245, + "junction_id": 4, + "route_id": 23, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 45.60164039420964, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.393674526515215 + } + }, + { + "event_id": "2f83a3cf", + "vehicle_id": "east_E_1_10", + "flow_name": "east", + "spawn_step": 1257, + "junction_id": 4, + "route_id": 141, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 48.32333305410143, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.2413781706862013 + } + }, + { + "event_id": "df16e17a", + "vehicle_id": "north_N_1_12", + "flow_name": "north", + "spawn_step": 1283, + "junction_id": 4, + "route_id": 34, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 55.280134096861175, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.3788537735600122 + } + }, + { + "event_id": "242e33b4", + "vehicle_id": "east_E_0_2", + "flow_name": "east", + "spawn_step": 1312, + "junction_id": 4, + "route_id": 128, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 55.31087098629768, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.292029328114049 + } + }, + { + "event_id": "265edf27", + "vehicle_id": "south_S_1_12", + "flow_name": "south", + "spawn_step": 1321, + "junction_id": 4, + "route_id": 63, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 61.78811589547177, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.170112612939307 + } + }, + { + "event_id": "940c17e2", + "vehicle_id": "west_W_0_3", + "flow_name": "west", + "spawn_step": 1342, + "junction_id": 4, + "route_id": 157, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 52.42925442036137, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.3378689949626212 + } + }, + { + "event_id": "e646d610", + "vehicle_id": "north_N_0_3", + "flow_name": "north", + "spawn_step": 1371, + "junction_id": 4, + "route_id": 20, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 46.9489050414942, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.3185562804599547 + } + }, + { + "event_id": "1df87109", + "vehicle_id": "north_N_2_18", + "flow_name": "north", + "spawn_step": 1394, + "junction_id": 4, + "route_id": 21, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 57.35706176302869, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.299006673880291 + } + }, + { + "event_id": "0fc35766", + "vehicle_id": "east_E_0_3", + "flow_name": "east", + "spawn_step": 1395, + "junction_id": 4, + "route_id": 130, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 57.78218788608871, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.3425314323599853 + } + }, + { + "event_id": "970f91eb", + "vehicle_id": "east_E_0_4", + "flow_name": "east", + "spawn_step": 1399, + "junction_id": 4, + "route_id": 103, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 49.694837918909144, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.3438130735944105 + } + }, + { + "event_id": "b97528ac", + "vehicle_id": "east_E_2_18", + "flow_name": "east", + "spawn_step": 1411, + "junction_id": 4, + "route_id": 107, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 53.82629181490955, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.3468186003756482 + } + }, + { + "event_id": "dfa14939", + "vehicle_id": "south_S_2_18", + "flow_name": "south", + "spawn_step": 1427, + "junction_id": 4, + "route_id": 77, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 51.18362603231056, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.116252916934548 + } + }, + { + "event_id": "6fe8090b", + "vehicle_id": "west_W_0_4", + "flow_name": "west", + "spawn_step": 1450, + "junction_id": 4, + "route_id": 191, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 58.0033089823678, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.229934882672922 + } + }, + { + "event_id": "c347a50a", + "vehicle_id": "west_W_0_5", + "flow_name": "west", + "spawn_step": 1468, + "junction_id": 4, + "route_id": 162, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 58.242180305591525, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.2123820466801964 + } + }, + { + "event_id": "15e369b8", + "vehicle_id": "west_W_0_6", + "flow_name": "west", + "spawn_step": 1485, + "junction_id": 4, + "route_id": 183, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 42.955510199333766, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.1968677002741652 + } + }, + { + "event_id": "5c45ef61", + "vehicle_id": "east_E_2_19", + "flow_name": "east", + "spawn_step": 1518, + "junction_id": 4, + "route_id": 121, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 56.7527511303723, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.3269462747267575 + } + }, + { + "event_id": "c61ebc29", + "vehicle_id": "east_E_1_11", + "flow_name": "east", + "spawn_step": 1575, + "junction_id": 4, + "route_id": 130, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 51.979336248896274, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.2854049062969788 + } + }, + { + "event_id": "60d1b6d1", + "vehicle_id": "west_W_2_19", + "flow_name": "west", + "spawn_step": 1610, + "junction_id": 4, + "route_id": 173, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 67.72316506866876, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.1014187935807163 + } + }, + { + "event_id": "1673a849", + "vehicle_id": "west_W_1_12", + "flow_name": "west", + "spawn_step": 1629, + "junction_id": 4, + "route_id": 188, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 54.349150853246876, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0904764203628732 + } + }, + { + "event_id": "562c6e7c", + "vehicle_id": "east_E_0_5", + "flow_name": "east", + "spawn_step": 1631, + "junction_id": 4, + "route_id": 112, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 49.51287200255448, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.232688798484225 + } + }, + { + "event_id": "55b4126a", + "vehicle_id": "north_N_0_4", + "flow_name": "north", + "spawn_step": 1657, + "junction_id": 4, + "route_id": 45, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 48.86005696757819, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0793960057265475 + } + }, + { + "event_id": "2f80dcfd", + "vehicle_id": "west_W_1_13", + "flow_name": "west", + "spawn_step": 1706, + "junction_id": 4, + "route_id": 159, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 58.97101356909379, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0540686643407169 + } + }, + { + "event_id": "80ffb410", + "vehicle_id": "north_N_0_5", + "flow_name": "north", + "spawn_step": 1741, + "junction_id": 4, + "route_id": 25, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 58.5321755870241, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0416844254264257 + } + }, + { + "event_id": "c85680a8", + "vehicle_id": "east_E_1_12", + "flow_name": "east", + "spawn_step": 1748, + "junction_id": 4, + "route_id": 100, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 55.670452562914704, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.1214196563965144 + } + }, + { + "event_id": "46d86cd3", + "vehicle_id": "north_N_1_13", + "flow_name": "north", + "spawn_step": 1770, + "junction_id": 4, + "route_id": 41, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 44.028399989891035, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.032533735155645 + } + }, + { + "event_id": "a8ce0796", + "vehicle_id": "south_S_1_13", + "flow_name": "south", + "spawn_step": 1793, + "junction_id": 4, + "route_id": 59, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 47.089555531003256, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0147203030513445 + } + }, + { + "event_id": "2c4b362e", + "vehicle_id": "east_E_0_6", + "flow_name": "east", + "spawn_step": 1795, + "junction_id": 4, + "route_id": 123, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 57.021457562775765, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.08563795623837 + } + }, + { + "event_id": "a1b4a76e", + "vehicle_id": "north_N_2_19", + "flow_name": "north", + "spawn_step": 1846, + "junction_id": 4, + "route_id": 16, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 54.66085556391642, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0158628001147907 + } + }, + { + "event_id": "025059a0", + "vehicle_id": "east_E_1_13", + "flow_name": "east", + "spawn_step": 2064, + "junction_id": 4, + "route_id": 138, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 57.417296893153456, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "5efcdfb4", + "vehicle_id": "south_S_2_19", + "flow_name": "south", + "spawn_step": 2113, + "junction_id": 4, + "route_id": 62, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 47.14046317257395, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "66dd1f7b", + "vehicle_id": "north_N_0_6", + "flow_name": "north", + "spawn_step": 2156, + "junction_id": 4, + "route_id": 19, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 50.95589831798079, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0 + } + } + ] +} \ No newline at end of file diff --git a/recordings/traffic_300vph.json b/recordings/traffic_300vph.json new file mode 100644 index 0000000..c72dd45 --- /dev/null +++ b/recordings/traffic_300vph.json @@ -0,0 +1,5047 @@ +{ + "version": "1.0", + "timestamp": "2025-12-02T15:30:16.060175", + "total_events": 120, + "events": [ + { + "event_id": "dbada832", + "vehicle_id": "east_E_2_20", + "flow_name": "east", + "spawn_step": 240, + "junction_id": 4, + "route_id": 136, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.62091435990575, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "49fb2a47", + "vehicle_id": "south_S_0_0", + "flow_name": "south", + "spawn_step": 286, + "junction_id": 4, + "route_id": 62, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 59.766207415329866, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0419217563111274 + } + }, + { + "event_id": "b0ac11be", + "vehicle_id": "west_W_0_0", + "flow_name": "west", + "spawn_step": 337, + "junction_id": 4, + "route_id": 190, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 48.52417435796688, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.021941396972252 + } + }, + { + "event_id": "81f6099e", + "vehicle_id": "north_N_2_20", + "flow_name": "north", + "spawn_step": 349, + "junction_id": 4, + "route_id": 18, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 58.60478439870998, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "09386e0a", + "vehicle_id": "east_E_2_21", + "flow_name": "east", + "spawn_step": 367, + "junction_id": 4, + "route_id": 115, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 58.360807600107826, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "2e971984", + "vehicle_id": "south_S_1_10", + "flow_name": "south", + "spawn_step": 399, + "junction_id": 4, + "route_id": 63, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 58.93024740452193, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.076731713838349 + } + }, + { + "event_id": "949417be", + "vehicle_id": "south_S_2_20", + "flow_name": "south", + "spawn_step": 406, + "junction_id": 4, + "route_id": 85, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 60.402232752258826, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0794673909724237 + } + }, + { + "event_id": "bb5e6e2b", + "vehicle_id": "east_E_1_10", + "flow_name": "east", + "spawn_step": 409, + "junction_id": 4, + "route_id": 111, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 58.73151730913058, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "756c1ee6", + "vehicle_id": "east_E_1_11", + "flow_name": "east", + "spawn_step": 428, + "junction_id": 4, + "route_id": 135, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 52.123994360777964, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "df402251", + "vehicle_id": "west_W_2_20", + "flow_name": "west", + "spawn_step": 451, + "junction_id": 4, + "route_id": 178, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 46.26909851141093, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0533732260251487 + } + }, + { + "event_id": "0cfb1df8", + "vehicle_id": "south_S_1_11", + "flow_name": "south", + "spawn_step": 507, + "junction_id": 4, + "route_id": 81, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 56.38797401642247, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.12320171155655 + } + }, + { + "event_id": "f5b93732", + "vehicle_id": "west_W_0_1", + "flow_name": "west", + "spawn_step": 516, + "junction_id": 4, + "route_id": 159, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 48.51664279024138, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0825592217693984 + } + }, + { + "event_id": "43b1ad23", + "vehicle_id": "east_E_2_22", + "flow_name": "east", + "spawn_step": 539, + "junction_id": 4, + "route_id": 132, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 53.319241418392934, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "0b0cab89", + "vehicle_id": "west_W_1_10", + "flow_name": "west", + "spawn_step": 542, + "junction_id": 4, + "route_id": 159, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 58.4404143744519, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0969145551304607 + } + }, + { + "event_id": "de7bef78", + "vehicle_id": "west_W_0_2", + "flow_name": "west", + "spawn_step": 559, + "junction_id": 4, + "route_id": 172, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 61.22203838375369, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.1076231764590576 + } + }, + { + "event_id": "ede9560f", + "vehicle_id": "west_W_2_21", + "flow_name": "west", + "spawn_step": 563, + "junction_id": 4, + "route_id": 191, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 48.31640318218137, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.1099888283474846 + } + }, + { + "event_id": "5628ea22", + "vehicle_id": "south_S_2_21", + "flow_name": "south", + "spawn_step": 574, + "junction_id": 4, + "route_id": 84, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 53.76125515376974, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.1572515249986692 + } + }, + { + "event_id": "172bca20", + "vehicle_id": "south_S_1_12", + "flow_name": "south", + "spawn_step": 581, + "junction_id": 4, + "route_id": 65, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 47.25445990245228, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.1611576113662483 + } + }, + { + "event_id": "568440d3", + "vehicle_id": "south_S_2_22", + "flow_name": "south", + "spawn_step": 582, + "junction_id": 4, + "route_id": 70, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 58.22730558914178, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.1613714465129754 + } + }, + { + "event_id": "f916e862", + "vehicle_id": "east_E_1_12", + "flow_name": "east", + "spawn_step": 590, + "junction_id": 4, + "route_id": 126, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 55.70366540123021, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "cee304a6", + "vehicle_id": "south_S_0_1", + "flow_name": "south", + "spawn_step": 614, + "junction_id": 4, + "route_id": 92, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 53.442744295881205, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.178687401170615 + } + }, + { + "event_id": "52161658", + "vehicle_id": "west_W_0_3", + "flow_name": "west", + "spawn_step": 671, + "junction_id": 4, + "route_id": 180, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 58.468650588781934, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.1939670943544536 + } + }, + { + "event_id": "cdfd9427", + "vehicle_id": "south_S_0_2", + "flow_name": "south", + "spawn_step": 682, + "junction_id": 4, + "route_id": 83, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 60.55978504228353, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.2146562689659557 + } + }, + { + "event_id": "427a7fe2", + "vehicle_id": "north_N_1_10", + "flow_name": "north", + "spawn_step": 685, + "junction_id": 4, + "route_id": 26, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 52.5788411235264, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0518359193086946 + } + }, + { + "event_id": "44586256", + "vehicle_id": "north_N_0_0", + "flow_name": "north", + "spawn_step": 693, + "junction_id": 4, + "route_id": 16, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 46.12938537774766, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0550763078282945 + } + }, + { + "event_id": "7b7b2109", + "vehicle_id": "south_S_1_13", + "flow_name": "south", + "spawn_step": 720, + "junction_id": 4, + "route_id": 66, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.03420639221072, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.2340456093603658 + } + }, + { + "event_id": "97e14624", + "vehicle_id": "west_W_1_11", + "flow_name": "west", + "spawn_step": 755, + "junction_id": 4, + "route_id": 175, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 61.32053981599634, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.2751718444026436 + } + }, + { + "event_id": "2cbe83c6", + "vehicle_id": "north_N_0_1", + "flow_name": "north", + "spawn_step": 756, + "junction_id": 4, + "route_id": 27, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 59.50098749901684, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.087793015163077 + } + }, + { + "event_id": "e679618b", + "vehicle_id": "east_E_0_0", + "flow_name": "east", + "spawn_step": 758, + "junction_id": 4, + "route_id": 133, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 56.79299732519778, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "df45f7f9", + "vehicle_id": "west_W_1_12", + "flow_name": "west", + "spawn_step": 774, + "junction_id": 4, + "route_id": 149, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 45.9509257117555, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2942248677093051 + } + }, + { + "event_id": "2a2e8f3a", + "vehicle_id": "south_S_2_23", + "flow_name": "south", + "spawn_step": 818, + "junction_id": 4, + "route_id": 74, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 57.459847719334626, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.2748900859959142 + } + }, + { + "event_id": "621a671f", + "vehicle_id": "east_E_0_1", + "flow_name": "east", + "spawn_step": 821, + "junction_id": 4, + "route_id": 101, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 50.9926943595362, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "6c0849cf", + "vehicle_id": "north_N_1_11", + "flow_name": "north", + "spawn_step": 823, + "junction_id": 4, + "route_id": 18, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 46.17318060647014, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.1340056686924431 + } + }, + { + "event_id": "1f3c1e3a", + "vehicle_id": "west_W_2_22", + "flow_name": "west", + "spawn_step": 826, + "junction_id": 4, + "route_id": 181, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 55.65919153293961, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.34729330832845 + } + }, + { + "event_id": "b4d4e810", + "vehicle_id": "west_W_2_23", + "flow_name": "west", + "spawn_step": 857, + "junction_id": 4, + "route_id": 150, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 47.532181432781705, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.3774732669470762 + } + }, + { + "event_id": "22aa5c6f", + "vehicle_id": "south_S_1_14", + "flow_name": "south", + "spawn_step": 863, + "junction_id": 4, + "route_id": 68, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 46.69402570753344, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.2881695401091173 + } + }, + { + "event_id": "4ed32231", + "vehicle_id": "south_S_1_15", + "flow_name": "south", + "spawn_step": 875, + "junction_id": 4, + "route_id": 57, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 51.52636532027983, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2908827228456423 + } + }, + { + "event_id": "14c27d71", + "vehicle_id": "south_S_1_16", + "flow_name": "south", + "spawn_step": 916, + "junction_id": 4, + "route_id": 57, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 46.387288159295316, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2975307552976991 + } + }, + { + "event_id": "2aba6894", + "vehicle_id": "west_W_1_13", + "flow_name": "west", + "spawn_step": 919, + "junction_id": 4, + "route_id": 188, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 47.52359538189175, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.4322612573980007 + } + }, + { + "event_id": "5902c43d", + "vehicle_id": "south_S_0_3", + "flow_name": "south", + "spawn_step": 920, + "junction_id": 4, + "route_id": 55, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 47.49133481144413, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.2980097225642195 + } + }, + { + "event_id": "95bd4696", + "vehicle_id": "west_W_0_4", + "flow_name": "west", + "spawn_step": 941, + "junction_id": 4, + "route_id": 165, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 52.38443097797259, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.4483669097743632 + } + }, + { + "event_id": "871d038d", + "vehicle_id": "west_W_0_5", + "flow_name": "west", + "spawn_step": 957, + "junction_id": 4, + "route_id": 164, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 52.14831362115413, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.4590959824845497 + } + }, + { + "event_id": "26642965", + "vehicle_id": "west_W_1_14", + "flow_name": "west", + "spawn_step": 959, + "junction_id": 4, + "route_id": 181, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 51.76562043996049, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.4605286096814662 + } + }, + { + "event_id": "c8ea3986", + "vehicle_id": "south_S_0_4", + "flow_name": "south", + "spawn_step": 967, + "junction_id": 4, + "route_id": 77, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 61.15917132961343, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.2999282130584753 + } + }, + { + "event_id": "1e8c7360", + "vehicle_id": "south_S_1_17", + "flow_name": "south", + "spawn_step": 996, + "junction_id": 4, + "route_id": 74, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 54.0418617474334, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.2982280256331182 + } + }, + { + "event_id": "69a504ac", + "vehicle_id": "north_N_1_12", + "flow_name": "north", + "spawn_step": 1007, + "junction_id": 4, + "route_id": 26, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 57.13054598927846, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.3005804592695775 + } + }, + { + "event_id": "4f18ec68", + "vehicle_id": "west_W_2_24", + "flow_name": "west", + "spawn_step": 1028, + "junction_id": 4, + "route_id": 167, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 47.12110869273593, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.4925613032106564 + } + }, + { + "event_id": "a648bde4", + "vehicle_id": "south_S_0_5", + "flow_name": "south", + "spawn_step": 1032, + "junction_id": 4, + "route_id": 86, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 60.59539252899979, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.2931905244948194 + } + }, + { + "event_id": "9f6803e3", + "vehicle_id": "west_W_2_25", + "flow_name": "west", + "spawn_step": 1045, + "junction_id": 4, + "route_id": 189, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 42.50928684721253, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.49668976531013 + } + }, + { + "event_id": "298d98f1", + "vehicle_id": "north_N_2_21", + "flow_name": "north", + "spawn_step": 1064, + "junction_id": 4, + "route_id": 39, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 44.45661234994405, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.3469596702287197 + } + }, + { + "event_id": "1e4842ae", + "vehicle_id": "east_E_2_23", + "flow_name": "east", + "spawn_step": 1066, + "junction_id": 4, + "route_id": 123, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 49.88532706557156, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0742554706519405 + } + }, + { + "event_id": "9786e72c", + "vehicle_id": "north_N_2_22", + "flow_name": "north", + "spawn_step": 1076, + "junction_id": 4, + "route_id": 15, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 47.201128677551644, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.3557195471035444 + } + }, + { + "event_id": "d2327b08", + "vehicle_id": "north_N_2_23", + "flow_name": "north", + "spawn_step": 1076, + "junction_id": 4, + "route_id": 12, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 42.95281468267661, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.355778586699654 + } + }, + { + "event_id": "73dd4f77", + "vehicle_id": "east_E_2_24", + "flow_name": "east", + "spawn_step": 1085, + "junction_id": 4, + "route_id": 137, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 49.20845895792963, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0867691104037798 + } + }, + { + "event_id": "f6dbf606", + "vehicle_id": "east_E_2_25", + "flow_name": "east", + "spawn_step": 1094, + "junction_id": 4, + "route_id": 122, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 50.19105217638758, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0927490717538608 + } + }, + { + "event_id": "a26e1e99", + "vehicle_id": "north_N_0_2", + "flow_name": "north", + "spawn_step": 1098, + "junction_id": 4, + "route_id": 20, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 55.21935309145953, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.3692653338880219 + } + }, + { + "event_id": "f7675b0e", + "vehicle_id": "north_N_1_13", + "flow_name": "north", + "spawn_step": 1116, + "junction_id": 4, + "route_id": 11, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 54.37693607199514, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3792228532399413 + } + }, + { + "event_id": "9001165c", + "vehicle_id": "west_W_0_6", + "flow_name": "west", + "spawn_step": 1122, + "junction_id": 4, + "route_id": 166, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 48.10492992344868, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.4948472639592973 + } + }, + { + "event_id": "8e0f2199", + "vehicle_id": "north_N_2_24", + "flow_name": "north", + "spawn_step": 1133, + "junction_id": 4, + "route_id": 15, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 55.22758778418111, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.3865127704750995 + } + }, + { + "event_id": "7d59dc0f", + "vehicle_id": "north_N_0_3", + "flow_name": "north", + "spawn_step": 1144, + "junction_id": 4, + "route_id": 32, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 50.42494401046638, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.390544617333294 + } + }, + { + "event_id": "f5128485", + "vehicle_id": "west_W_2_26", + "flow_name": "west", + "spawn_step": 1160, + "junction_id": 4, + "route_id": 146, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 53.746401336916456, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.4818659295757877 + } + }, + { + "event_id": "018c8bc6", + "vehicle_id": "east_E_2_26", + "flow_name": "east", + "spawn_step": 1165, + "junction_id": 4, + "route_id": 119, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 56.41332372906995, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.1513731444195352 + } + }, + { + "event_id": "962b8787", + "vehicle_id": "north_N_1_14", + "flow_name": "north", + "spawn_step": 1167, + "junction_id": 4, + "route_id": 25, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 59.86879175367594, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.396709089666191 + } + }, + { + "event_id": "071f08ab", + "vehicle_id": "east_E_1_13", + "flow_name": "east", + "spawn_step": 1179, + "junction_id": 4, + "route_id": 109, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 56.252845079441144, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.1645745562234635 + } + }, + { + "event_id": "42a010e0", + "vehicle_id": "west_W_2_27", + "flow_name": "west", + "spawn_step": 1187, + "junction_id": 4, + "route_id": 188, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 56.03843056745433, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.46826308293886 + } + }, + { + "event_id": "abca50ff", + "vehicle_id": "north_N_1_15", + "flow_name": "north", + "spawn_step": 1188, + "junction_id": 4, + "route_id": 19, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 46.681077354818896, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.3995847628114209 + } + }, + { + "event_id": "ea690341", + "vehicle_id": "north_N_1_16", + "flow_name": "north", + "spawn_step": 1193, + "junction_id": 4, + "route_id": 37, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 56.06822600242144, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.3998556200450203 + } + }, + { + "event_id": "6cb74860", + "vehicle_id": "north_N_1_17", + "flow_name": "north", + "spawn_step": 1198, + "junction_id": 4, + "route_id": 29, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 51.57847533572873, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.3999887628507404 + } + }, + { + "event_id": "5fb22e88", + "vehicle_id": "north_N_0_4", + "flow_name": "north", + "spawn_step": 1206, + "junction_id": 4, + "route_id": 36, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 47.62029285651185, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.3998884106049545 + } + }, + { + "event_id": "23c50adc", + "vehicle_id": "west_W_2_28", + "flow_name": "west", + "spawn_step": 1213, + "junction_id": 4, + "route_id": 175, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 62.30255145422063, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.4516859189464189 + } + }, + { + "event_id": "b8b223d5", + "vehicle_id": "north_N_2_25", + "flow_name": "north", + "spawn_step": 1221, + "junction_id": 4, + "route_id": 4, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 51.000499130642325, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.3985871041001905 + } + }, + { + "event_id": "9a87a8d3", + "vehicle_id": "south_S_0_6", + "flow_name": "south", + "spawn_step": 1231, + "junction_id": 4, + "route_id": 76, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 58.88638047952323, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.2178204331891471 + } + }, + { + "event_id": "d802b1d1", + "vehicle_id": "west_W_0_7", + "flow_name": "west", + "spawn_step": 1245, + "junction_id": 4, + "route_id": 182, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 54.114637289010965, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.4279911449995588 + } + }, + { + "event_id": "3d7f5d59", + "vehicle_id": "west_W_0_8", + "flow_name": "west", + "spawn_step": 1274, + "junction_id": 4, + "route_id": 177, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 41.843248797817374, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.4038834188258948 + } + }, + { + "event_id": "ed99f646", + "vehicle_id": "north_N_1_18", + "flow_name": "north", + "spawn_step": 1285, + "junction_id": 4, + "route_id": 13, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 52.35255623069486, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3781540572739905 + } + }, + { + "event_id": "984a5858", + "vehicle_id": "south_S_2_24", + "flow_name": "south", + "spawn_step": 1309, + "junction_id": 4, + "route_id": 82, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 60.477264716839755, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.1765032333241985 + } + }, + { + "event_id": "8f9f5e85", + "vehicle_id": "west_W_0_9", + "flow_name": "west", + "spawn_step": 1318, + "junction_id": 4, + "route_id": 190, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 52.70661690932288, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.3619350165986002 + } + }, + { + "event_id": "8c00ebfa", + "vehicle_id": "east_E_1_14", + "flow_name": "east", + "spawn_step": 1370, + "junction_id": 4, + "route_id": 119, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 54.300416608359924, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3319521049793517 + } + }, + { + "event_id": "4b148dd3", + "vehicle_id": "east_E_0_2", + "flow_name": "east", + "spawn_step": 1382, + "junction_id": 4, + "route_id": 125, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 48.363845160419885, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.3374985291174601 + } + }, + { + "event_id": "2ec18176", + "vehicle_id": "north_N_2_26", + "flow_name": "north", + "spawn_step": 1390, + "junction_id": 4, + "route_id": 40, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 46.819906557798134, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.3019349344259648 + } + }, + { + "event_id": "f8153aa8", + "vehicle_id": "east_E_0_3", + "flow_name": "east", + "spawn_step": 1422, + "junction_id": 4, + "route_id": 120, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 50.02030651278745, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.3488360412796627 + } + }, + { + "event_id": "8fcfe17a", + "vehicle_id": "east_E_0_4", + "flow_name": "east", + "spawn_step": 1430, + "junction_id": 4, + "route_id": 116, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 55.85002300029786, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.349642383812138 + } + }, + { + "event_id": "74eaffd4", + "vehicle_id": "west_W_2_29", + "flow_name": "west", + "spawn_step": 1433, + "junction_id": 4, + "route_id": 180, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 52.82627040972629, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.2459720343773002 + } + }, + { + "event_id": "1dd640f5", + "vehicle_id": "south_S_2_25", + "flow_name": "south", + "spawn_step": 1464, + "junction_id": 4, + "route_id": 79, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.744438026469204, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0993093713662094 + } + }, + { + "event_id": "3054f8f4", + "vehicle_id": "east_E_2_27", + "flow_name": "east", + "spawn_step": 1470, + "junction_id": 4, + "route_id": 123, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 55.57812403525681, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.3462851774056237 + } + }, + { + "event_id": "69dcaf5e", + "vehicle_id": "east_E_0_5", + "flow_name": "east", + "spawn_step": 1475, + "junction_id": 4, + "route_id": 120, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 55.77846790832127, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.3450869663163085 + } + }, + { + "event_id": "41292483", + "vehicle_id": "north_N_0_5", + "flow_name": "north", + "spawn_step": 1497, + "junction_id": 4, + "route_id": 45, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 43.146818642436266, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.2021902650892218 + } + }, + { + "event_id": "ba5eff2f", + "vehicle_id": "north_N_0_6", + "flow_name": "north", + "spawn_step": 1519, + "junction_id": 4, + "route_id": 34, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 51.068494249271154, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.181566232880177 + } + }, + { + "event_id": "471f0ee3", + "vehicle_id": "south_S_0_7", + "flow_name": "south", + "spawn_step": 1522, + "junction_id": 4, + "route_id": 80, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 59.995862261267554, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0758471021331313 + } + }, + { + "event_id": "ba257e7f", + "vehicle_id": "west_W_1_15", + "flow_name": "west", + "spawn_step": 1566, + "junction_id": 4, + "route_id": 177, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 52.25791702068359, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.1309575233121258 + } + }, + { + "event_id": "cbd67bfc", + "vehicle_id": "east_E_0_6", + "flow_name": "east", + "spawn_step": 1599, + "junction_id": 4, + "route_id": 122, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 57.136875627255506, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.2636398179759416 + } + }, + { + "event_id": "70b53b6f", + "vehicle_id": "west_W_1_16", + "flow_name": "west", + "spawn_step": 1612, + "junction_id": 4, + "route_id": 191, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 49.84281794450514, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.1000943698557182 + } + }, + { + "event_id": "8e82b96c", + "vehicle_id": "north_N_2_27", + "flow_name": "north", + "spawn_step": 1639, + "junction_id": 4, + "route_id": 44, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 54.381630577901, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.090142221851144 + } + }, + { + "event_id": "5fc4561d", + "vehicle_id": "north_N_0_7", + "flow_name": "north", + "spawn_step": 1670, + "junction_id": 4, + "route_id": 4, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 57.29043518503914, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0726023309375585 + } + }, + { + "event_id": "26113d88", + "vehicle_id": "east_E_1_15", + "flow_name": "east", + "spawn_step": 1684, + "junction_id": 4, + "route_id": 143, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 57.00781771979328, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.180497838134528 + } + }, + { + "event_id": "91702495", + "vehicle_id": "south_S_2_26", + "flow_name": "south", + "spawn_step": 1693, + "junction_id": 4, + "route_id": 79, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 53.012111816314864, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.028971827949475 + } + }, + { + "event_id": "8d9e2d10", + "vehicle_id": "south_S_2_27", + "flow_name": "south", + "spawn_step": 1695, + "junction_id": 4, + "route_id": 90, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 58.5303389216742, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0287319767051866 + } + }, + { + "event_id": "286da157", + "vehicle_id": "south_S_2_28", + "flow_name": "south", + "spawn_step": 1705, + "junction_id": 4, + "route_id": 77, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 62.76238961342693, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0269375279652653 + } + }, + { + "event_id": "dc5dd707", + "vehicle_id": "south_S_1_18", + "flow_name": "south", + "spawn_step": 1716, + "junction_id": 4, + "route_id": 74, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 50.036822838957185, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0251071849681928 + } + }, + { + "event_id": "713c23f7", + "vehicle_id": "north_N_0_8", + "flow_name": "north", + "spawn_step": 1719, + "junction_id": 4, + "route_id": 42, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 59.53639024611149, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0498379818972998 + } + }, + { + "event_id": "2f93f9d2", + "vehicle_id": "east_E_2_28", + "flow_name": "east", + "spawn_step": 1723, + "junction_id": 4, + "route_id": 137, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 55.46934910015787, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.1431694856561891 + } + }, + { + "event_id": "bcb1bbe1", + "vehicle_id": "west_W_1_17", + "flow_name": "west", + "spawn_step": 1724, + "junction_id": 4, + "route_id": 188, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 48.469831498534866, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0473037554406246 + } + }, + { + "event_id": "84e00a0f", + "vehicle_id": "south_S_0_8", + "flow_name": "south", + "spawn_step": 1732, + "junction_id": 4, + "route_id": 83, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 61.41536494354087, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0225117635008942 + } + }, + { + "event_id": "399528bb", + "vehicle_id": "east_E_1_16", + "flow_name": "east", + "spawn_step": 1756, + "junction_id": 4, + "route_id": 106, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 50.107839099384044, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.1146690914935993 + } + }, + { + "event_id": "95098e93", + "vehicle_id": "east_E_0_7", + "flow_name": "east", + "spawn_step": 1763, + "junction_id": 4, + "route_id": 126, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 57.10778627988553, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.1093676151840837 + } + }, + { + "event_id": "628fb2b9", + "vehicle_id": "north_N_2_28", + "flow_name": "north", + "spawn_step": 1788, + "junction_id": 4, + "route_id": 15, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 58.27234570868923, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0275409058182456 + } + }, + { + "event_id": "a6f84079", + "vehicle_id": "east_E_2_29", + "flow_name": "east", + "spawn_step": 1855, + "junction_id": 4, + "route_id": 130, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 54.33416028903857, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0511687821638596 + } + }, + { + "event_id": "09c8c4a7", + "vehicle_id": "east_E_0_8", + "flow_name": "east", + "spawn_step": 1863, + "junction_id": 4, + "route_id": 99, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 51.71821795736513, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0477665676716232 + } + }, + { + "event_id": "4ac95e98", + "vehicle_id": "north_N_0_9", + "flow_name": "north", + "spawn_step": 1892, + "junction_id": 4, + "route_id": 19, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 50.753888343577714, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0099153540403532 + } + }, + { + "event_id": "0b15ff9f", + "vehicle_id": "north_N_1_19", + "flow_name": "north", + "spawn_step": 1901, + "junction_id": 4, + "route_id": 47, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 58.44204175777915, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.009005023946943 + } + }, + { + "event_id": "6f301d54", + "vehicle_id": "west_W_1_18", + "flow_name": "west", + "spawn_step": 1902, + "junction_id": 4, + "route_id": 160, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 44.51978334509308, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0108269264312997 + } + }, + { + "event_id": "ceb11dce", + "vehicle_id": "north_N_2_29", + "flow_name": "north", + "spawn_step": 1918, + "junction_id": 4, + "route_id": 0, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 61.07981032610852, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0074336912411808 + } + }, + { + "event_id": "940c786a", + "vehicle_id": "east_E_0_9", + "flow_name": "east", + "spawn_step": 1926, + "junction_id": 4, + "route_id": 100, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 57.65141166513354, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0251145684707745 + } + }, + { + "event_id": "52b2f1a7", + "vehicle_id": "south_S_0_9", + "flow_name": "south", + "spawn_step": 1943, + "junction_id": 4, + "route_id": 95, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 54.17615099457398, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "2054e1cf", + "vehicle_id": "east_E_1_17", + "flow_name": "east", + "spawn_step": 1945, + "junction_id": 4, + "route_id": 97, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 55.7192186097492, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0205154362800326 + } + }, + { + "event_id": "39dcb67b", + "vehicle_id": "west_W_1_19", + "flow_name": "west", + "spawn_step": 2005, + "junction_id": 4, + "route_id": 156, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 44.53775538451395, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "a1921955", + "vehicle_id": "east_E_1_18", + "flow_name": "east", + "spawn_step": 2058, + "junction_id": 4, + "route_id": 120, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 59.8561765839544, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "a9a9a6e8", + "vehicle_id": "south_S_2_29", + "flow_name": "south", + "spawn_step": 2082, + "junction_id": 4, + "route_id": 82, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 61.71432623268168, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "416afb8e", + "vehicle_id": "south_S_1_19", + "flow_name": "south", + "spawn_step": 2096, + "junction_id": 4, + "route_id": 74, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 61.679834892612924, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "23e57d35", + "vehicle_id": "east_E_1_19", + "flow_name": "east", + "spawn_step": 2100, + "junction_id": 4, + "route_id": 108, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 56.01307647374838, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0 + } + } + ] +} \ No newline at end of file diff --git a/recordings/traffic_400vph.json b/recordings/traffic_400vph.json new file mode 100644 index 0000000..294adff --- /dev/null +++ b/recordings/traffic_400vph.json @@ -0,0 +1,6727 @@ +{ + "version": "1.0", + "timestamp": "2025-12-02T15:23:57.826958", + "total_events": 160, + "events": [ + { + "event_id": "55d30eeb", + "vehicle_id": "north_N_2_27", + "flow_name": "north", + "spawn_step": 253, + "junction_id": 4, + "route_id": 40, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 46.24182871981148, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "24baacfa", + "vehicle_id": "east_E_1_14", + "flow_name": "east", + "spawn_step": 254, + "junction_id": 4, + "route_id": 113, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 55.439797188578154, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "812a24e7", + "vehicle_id": "west_W_2_27", + "flow_name": "west", + "spawn_step": 262, + "junction_id": 4, + "route_id": 164, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 65.16669017014829, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0113185476789144 + } + }, + { + "event_id": "cc2394e2", + "vehicle_id": "south_S_1_14", + "flow_name": "south", + "spawn_step": 263, + "junction_id": 4, + "route_id": 92, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 51.03302019085357, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.036528780181698 + } + }, + { + "event_id": "49c9b05b", + "vehicle_id": "east_E_1_15", + "flow_name": "east", + "spawn_step": 265, + "junction_id": 4, + "route_id": 124, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 56.078403282452136, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "1e988fc8", + "vehicle_id": "south_S_0_0", + "flow_name": "south", + "spawn_step": 269, + "junction_id": 4, + "route_id": 84, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 57.55629386725156, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0378737307465373 + } + }, + { + "event_id": "6381da48", + "vehicle_id": "east_E_2_27", + "flow_name": "east", + "spawn_step": 279, + "junction_id": 4, + "route_id": 121, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 49.9036684334508, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "dd335a79", + "vehicle_id": "south_S_1_15", + "flow_name": "south", + "spawn_step": 319, + "junction_id": 4, + "route_id": 64, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 48.49448978270573, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0505392205346769 + } + }, + { + "event_id": "fff02537", + "vehicle_id": "west_W_1_14", + "flow_name": "west", + "spawn_step": 341, + "junction_id": 4, + "route_id": 156, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 56.84492604067924, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0227310922090138 + } + }, + { + "event_id": "a248f75b", + "vehicle_id": "north_N_2_28", + "flow_name": "north", + "spawn_step": 352, + "junction_id": 4, + "route_id": 19, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 61.721615200215666, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "619411d0", + "vehicle_id": "west_W_2_28", + "flow_name": "west", + "spawn_step": 355, + "junction_id": 4, + "route_id": 146, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 43.0212872869475, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.0255690342700905 + } + }, + { + "event_id": "e88fdb11", + "vehicle_id": "south_S_1_16", + "flow_name": "south", + "spawn_step": 360, + "junction_id": 4, + "route_id": 60, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 48.41526310527696, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0630897755046038 + } + }, + { + "event_id": "19741eee", + "vehicle_id": "west_W_0_0", + "flow_name": "west", + "spawn_step": 360, + "junction_id": 4, + "route_id": 151, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 54.80959274894151, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.026518563369112 + } + }, + { + "event_id": "387b45d5", + "vehicle_id": "south_S_2_27", + "flow_name": "south", + "spawn_step": 366, + "junction_id": 4, + "route_id": 76, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 49.46842904863633, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.064984637924074 + } + }, + { + "event_id": "bdea5a12", + "vehicle_id": "south_S_1_17", + "flow_name": "south", + "spawn_step": 395, + "junction_id": 4, + "route_id": 82, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 49.53499620814997, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.075220725648136 + } + }, + { + "event_id": "3df0e05f", + "vehicle_id": "east_E_2_28", + "flow_name": "east", + "spawn_step": 412, + "junction_id": 4, + "route_id": 101, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 52.27638736036428, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "3f614df7", + "vehicle_id": "west_W_2_29", + "flow_name": "west", + "spawn_step": 413, + "junction_id": 4, + "route_id": 184, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 68.40227405279859, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0403235297667142 + } + }, + { + "event_id": "a8e091ac", + "vehicle_id": "east_E_2_29", + "flow_name": "east", + "spawn_step": 415, + "junction_id": 4, + "route_id": 103, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 59.01401642457066, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "09e3001a", + "vehicle_id": "east_E_0_0", + "flow_name": "east", + "spawn_step": 423, + "junction_id": 4, + "route_id": 109, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 52.41110737819008, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "f8c68d6b", + "vehicle_id": "east_E_0_1", + "flow_name": "east", + "spawn_step": 444, + "junction_id": 4, + "route_id": 101, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 52.13851695442162, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "2f70bb23", + "vehicle_id": "south_S_1_18", + "flow_name": "south", + "spawn_step": 463, + "junction_id": 4, + "route_id": 95, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 50.7842978104997, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.1028889003721252 + } + }, + { + "event_id": "144e47a9", + "vehicle_id": "south_S_2_28", + "flow_name": "south", + "spawn_step": 469, + "junction_id": 4, + "route_id": 51, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 61.618736081447246, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.1057866393488147 + } + }, + { + "event_id": "fbf6c325", + "vehicle_id": "north_N_0_0", + "flow_name": "north", + "spawn_step": 473, + "junction_id": 4, + "route_id": 24, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 55.840852487425096, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "66238ca4", + "vehicle_id": "west_W_2_30", + "flow_name": "west", + "spawn_step": 488, + "junction_id": 4, + "route_id": 191, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 52.818017189840354, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.068854693964202 + } + }, + { + "event_id": "c8636556", + "vehicle_id": "north_N_0_1", + "flow_name": "north", + "spawn_step": 506, + "junction_id": 4, + "route_id": 47, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 61.53710867699636, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0097392708332984 + } + }, + { + "event_id": "ad80543a", + "vehicle_id": "south_S_0_1", + "flow_name": "south", + "spawn_step": 511, + "junction_id": 4, + "route_id": 69, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 54.2185739428894, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.125210842019852 + } + }, + { + "event_id": "25b8ae47", + "vehicle_id": "east_E_0_2", + "flow_name": "east", + "spawn_step": 524, + "junction_id": 4, + "route_id": 131, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 51.221858769874515, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "8ee12985", + "vehicle_id": "east_E_0_3", + "flow_name": "east", + "spawn_step": 527, + "junction_id": 4, + "route_id": 122, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 57.08308249363246, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "ecea1273", + "vehicle_id": "east_E_1_16", + "flow_name": "east", + "spawn_step": 527, + "junction_id": 4, + "route_id": 123, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 56.140850050405014, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "0d3016ea", + "vehicle_id": "east_E_2_30", + "flow_name": "east", + "spawn_step": 540, + "junction_id": 4, + "route_id": 96, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 54.58173979480358, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "f9083f1f", + "vehicle_id": "west_W_0_1", + "flow_name": "west", + "spawn_step": 551, + "junction_id": 4, + "route_id": 189, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 54.678239120456674, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.102407771799027 + } + }, + { + "event_id": "e2ef7564", + "vehicle_id": "west_W_0_2", + "flow_name": "west", + "spawn_step": 555, + "junction_id": 4, + "route_id": 161, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 63.5293766706562, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.1052149825379112 + } + }, + { + "event_id": "e82ac5e8", + "vehicle_id": "east_E_1_17", + "flow_name": "east", + "spawn_step": 571, + "junction_id": 4, + "route_id": 97, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 52.0181685729401, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "213e1076", + "vehicle_id": "south_S_1_19", + "flow_name": "south", + "spawn_step": 578, + "junction_id": 4, + "route_id": 85, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 55.69556463940918, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.1592808500352088 + } + }, + { + "event_id": "09a966d3", + "vehicle_id": "north_N_1_14", + "flow_name": "north", + "spawn_step": 601, + "junction_id": 4, + "route_id": 30, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 60.325823272875645, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0252503593413702 + } + }, + { + "event_id": "bc12751e", + "vehicle_id": "north_N_0_2", + "flow_name": "north", + "spawn_step": 608, + "junction_id": 4, + "route_id": 14, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 48.69631275202706, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0268217317196522 + } + }, + { + "event_id": "0871ec49", + "vehicle_id": "west_W_1_15", + "flow_name": "west", + "spawn_step": 612, + "junction_id": 4, + "route_id": 158, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 41.836117035139125, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.1446830009551454 + } + }, + { + "event_id": "e326c559", + "vehicle_id": "south_S_2_29", + "flow_name": "south", + "spawn_step": 615, + "junction_id": 4, + "route_id": 78, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 59.23271870762743, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.179423209460544 + } + }, + { + "event_id": "01c5a70c", + "vehicle_id": "west_W_1_16", + "flow_name": "west", + "spawn_step": 617, + "junction_id": 4, + "route_id": 191, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 59.74194062487498, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.1487087877233426 + } + }, + { + "event_id": "5fb4a06c", + "vehicle_id": "south_S_2_30", + "flow_name": "south", + "spawn_step": 622, + "junction_id": 4, + "route_id": 73, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 47.09506361493388, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.182874528358186 + } + }, + { + "event_id": "dc51c53a", + "vehicle_id": "west_W_1_17", + "flow_name": "west", + "spawn_step": 625, + "junction_id": 4, + "route_id": 168, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 67.37072673449596, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.1549958864649437 + } + }, + { + "event_id": "a2f70a7c", + "vehicle_id": "west_W_0_3", + "flow_name": "west", + "spawn_step": 627, + "junction_id": 4, + "route_id": 174, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 52.967720174615735, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.1568262951495742 + } + }, + { + "event_id": "85791d64", + "vehicle_id": "north_N_0_3", + "flow_name": "north", + "spawn_step": 642, + "junction_id": 4, + "route_id": 27, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 44.47649217782934, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0363035706533634 + } + }, + { + "event_id": "ee3bc8d6", + "vehicle_id": "east_E_0_4", + "flow_name": "east", + "spawn_step": 649, + "junction_id": 4, + "route_id": 126, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 49.85903840919017, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "0a5f331d", + "vehicle_id": "south_S_1_20", + "flow_name": "south", + "spawn_step": 669, + "junction_id": 4, + "route_id": 54, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.56306791067517, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2081795084648503 + } + }, + { + "event_id": "40896d7a", + "vehicle_id": "south_S_2_31", + "flow_name": "south", + "spawn_step": 690, + "junction_id": 4, + "route_id": 88, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 48.17589586439516, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.2187864509608948 + } + }, + { + "event_id": "ec24eca2", + "vehicle_id": "north_N_0_4", + "flow_name": "north", + "spawn_step": 691, + "junction_id": 4, + "route_id": 21, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 59.606559551225544, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0543000082098817 + } + }, + { + "event_id": "2d4faddf", + "vehicle_id": "west_W_1_18", + "flow_name": "west", + "spawn_step": 708, + "junction_id": 4, + "route_id": 185, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 53.61506562158937, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.228984331725151 + } + }, + { + "event_id": "5d8f20d9", + "vehicle_id": "south_S_0_2", + "flow_name": "south", + "spawn_step": 715, + "junction_id": 4, + "route_id": 68, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 53.52555356553287, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.2315959827701617 + } + }, + { + "event_id": "f2407481", + "vehicle_id": "west_W_0_4", + "flow_name": "west", + "spawn_step": 715, + "junction_id": 4, + "route_id": 153, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 45.39559492595327, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.2357814978224528 + } + }, + { + "event_id": "b1014d17", + "vehicle_id": "west_W_1_19", + "flow_name": "west", + "spawn_step": 715, + "junction_id": 4, + "route_id": 146, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 53.768862164687086, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2350297492283997 + } + }, + { + "event_id": "617c690e", + "vehicle_id": "north_N_1_15", + "flow_name": "north", + "spawn_step": 731, + "junction_id": 4, + "route_id": 3, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 48.57218287195141, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0737141810419497 + } + }, + { + "event_id": "298b9554", + "vehicle_id": "north_N_2_29", + "flow_name": "north", + "spawn_step": 743, + "junction_id": 4, + "route_id": 36, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 51.22546964517971, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.07998166656282 + } + }, + { + "event_id": "16a1ae71", + "vehicle_id": "south_S_0_3", + "flow_name": "south", + "spawn_step": 747, + "junction_id": 4, + "route_id": 70, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 51.07504172313622, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.2464498474271057 + } + }, + { + "event_id": "35df78da", + "vehicle_id": "north_N_1_16", + "flow_name": "north", + "spawn_step": 749, + "junction_id": 4, + "route_id": 4, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 60.08699383399882, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.083530369073778 + } + }, + { + "event_id": "29f5676c", + "vehicle_id": "east_E_0_5", + "flow_name": "east", + "spawn_step": 755, + "junction_id": 4, + "route_id": 114, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 57.23286313903088, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "e248e5b1", + "vehicle_id": "south_S_0_4", + "flow_name": "south", + "spawn_step": 765, + "junction_id": 4, + "route_id": 86, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 54.49692976805795, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.2545794243262043 + } + }, + { + "event_id": "3eae217c", + "vehicle_id": "south_S_1_21", + "flow_name": "south", + "spawn_step": 782, + "junction_id": 4, + "route_id": 71, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.38367755468866, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.2616554488789506 + } + }, + { + "event_id": "77d19738", + "vehicle_id": "south_S_0_5", + "flow_name": "south", + "spawn_step": 806, + "junction_id": 4, + "route_id": 81, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 46.8574559818966, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.270989837443436 + } + }, + { + "event_id": "9d5e1275", + "vehicle_id": "north_N_2_30", + "flow_name": "north", + "spawn_step": 819, + "junction_id": 4, + "route_id": 27, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 44.921558110385924, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.1308279241625485 + } + }, + { + "event_id": "e4d67a3e", + "vehicle_id": "south_S_1_22", + "flow_name": "south", + "spawn_step": 838, + "junction_id": 4, + "route_id": 93, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 47.464361229227244, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.2813874841213737 + } + }, + { + "event_id": "34f33c3e", + "vehicle_id": "north_N_0_5", + "flow_name": "north", + "spawn_step": 853, + "junction_id": 4, + "route_id": 29, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 64.53938500908225, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.1580609274213514 + } + }, + { + "event_id": "ed692f59", + "vehicle_id": "south_S_2_32", + "flow_name": "south", + "spawn_step": 853, + "junction_id": 4, + "route_id": 84, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 46.78075768473526, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.2854767850702262 + } + }, + { + "event_id": "2c78afcf", + "vehicle_id": "south_S_0_6", + "flow_name": "south", + "spawn_step": 860, + "junction_id": 4, + "route_id": 94, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 53.23654157531906, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.2873469814025404 + } + }, + { + "event_id": "7e4227dd", + "vehicle_id": "east_E_0_6", + "flow_name": "east", + "spawn_step": 871, + "junction_id": 4, + "route_id": 125, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 57.0060784548642, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0096660485475517 + } + }, + { + "event_id": "14bf77d6", + "vehicle_id": "south_S_1_23", + "flow_name": "south", + "spawn_step": 898, + "junction_id": 4, + "route_id": 50, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 54.23147660625522, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.295084800509294 + } + }, + { + "event_id": "aa273f3d", + "vehicle_id": "west_W_2_31", + "flow_name": "west", + "spawn_step": 898, + "junction_id": 4, + "route_id": 150, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 51.23464172034507, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.4146810739511286 + } + }, + { + "event_id": "76320454", + "vehicle_id": "north_N_0_6", + "flow_name": "north", + "spawn_step": 908, + "junction_id": 4, + "route_id": 34, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 62.62318752196423, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.2078493892256987 + } + }, + { + "event_id": "d8fde808", + "vehicle_id": "south_S_1_24", + "flow_name": "south", + "spawn_step": 942, + "junction_id": 4, + "route_id": 92, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 55.558898588858725, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.2995993426580712 + } + }, + { + "event_id": "4c7828e2", + "vehicle_id": "west_W_2_32", + "flow_name": "west", + "spawn_step": 942, + "junction_id": 4, + "route_id": 188, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 63.12713557428706, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.4489089589004531 + } + }, + { + "event_id": "5963d40b", + "vehicle_id": "east_E_2_31", + "flow_name": "east", + "spawn_step": 965, + "junction_id": 4, + "route_id": 99, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 55.04507415740975, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.0285780910469116 + } + }, + { + "event_id": "e5379004", + "vehicle_id": "west_W_2_33", + "flow_name": "west", + "spawn_step": 1012, + "junction_id": 4, + "route_id": 146, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 60.06635149462174, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.4873933332972942 + } + }, + { + "event_id": "2ed1f54e", + "vehicle_id": "west_W_1_20", + "flow_name": "west", + "spawn_step": 1013, + "junction_id": 4, + "route_id": 181, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 61.98821547250481, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.4877552930957707 + } + }, + { + "event_id": "61695094", + "vehicle_id": "west_W_0_5", + "flow_name": "west", + "spawn_step": 1035, + "junction_id": 4, + "route_id": 182, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 58.718587497389876, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.4944644551032857 + } + }, + { + "event_id": "6a0bd9b6", + "vehicle_id": "west_W_0_6", + "flow_name": "west", + "spawn_step": 1055, + "junction_id": 4, + "route_id": 152, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 46.413035957708075, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.4982450112690304 + } + }, + { + "event_id": "f02bc5e4", + "vehicle_id": "north_N_1_17", + "flow_name": "north", + "spawn_step": 1076, + "junction_id": 4, + "route_id": 15, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 60.243553582677826, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3553271225369619 + } + }, + { + "event_id": "97e6f023", + "vehicle_id": "south_S_0_7", + "flow_name": "south", + "spawn_step": 1079, + "junction_id": 4, + "route_id": 63, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 55.7880426433576, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.2820812757825915 + } + }, + { + "event_id": "a5617101", + "vehicle_id": "north_N_2_31", + "flow_name": "north", + "spawn_step": 1087, + "junction_id": 4, + "route_id": 41, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 42.67154556280485, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.3628865661640248 + } + }, + { + "event_id": "202761f9", + "vehicle_id": "west_W_2_34", + "flow_name": "west", + "spawn_step": 1091, + "junction_id": 4, + "route_id": 162, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 48.98222202126211, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.4996123694006702 + } + }, + { + "event_id": "66744ed7", + "vehicle_id": "north_N_0_7", + "flow_name": "north", + "spawn_step": 1095, + "junction_id": 4, + "route_id": 37, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 56.430431133490565, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.3679315388887796 + } + }, + { + "event_id": "04122dbc", + "vehicle_id": "north_N_2_32", + "flow_name": "north", + "spawn_step": 1115, + "junction_id": 4, + "route_id": 26, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 44.56830320419983, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.3785582300475636 + } + }, + { + "event_id": "753895e8", + "vehicle_id": "west_W_2_35", + "flow_name": "west", + "spawn_step": 1120, + "junction_id": 4, + "route_id": 153, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 53.089972768482966, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.4953895352695539 + } + }, + { + "event_id": "9f24c083", + "vehicle_id": "west_W_0_7", + "flow_name": "west", + "spawn_step": 1128, + "junction_id": 4, + "route_id": 170, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 63.63950089107099, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.4933133579463458 + } + }, + { + "event_id": "6e846b7f", + "vehicle_id": "west_W_0_8", + "flow_name": "west", + "spawn_step": 1139, + "junction_id": 4, + "route_id": 176, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 60.68826562899741, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.4899850299543793 + } + }, + { + "event_id": "1b535280", + "vehicle_id": "east_E_1_18", + "flow_name": "east", + "spawn_step": 1158, + "junction_id": 4, + "route_id": 139, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 55.147785718515735, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.1448517509393168 + } + }, + { + "event_id": "a188c878", + "vehicle_id": "north_N_2_33", + "flow_name": "north", + "spawn_step": 1168, + "junction_id": 4, + "route_id": 23, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 57.19610171514354, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.3970209998854817 + } + }, + { + "event_id": "37065df6", + "vehicle_id": "north_N_1_18", + "flow_name": "north", + "spawn_step": 1173, + "junction_id": 4, + "route_id": 39, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 47.28902861582179, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.397774641480271 + } + }, + { + "event_id": "11b4a026", + "vehicle_id": "south_S_0_8", + "flow_name": "south", + "spawn_step": 1192, + "junction_id": 4, + "route_id": 79, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 58.413913669223426, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.2372385385185816 + } + }, + { + "event_id": "03d8d498", + "vehicle_id": "west_W_0_9", + "flow_name": "west", + "spawn_step": 1198, + "junction_id": 4, + "route_id": 162, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 61.313264505952404, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.461620446229872 + } + }, + { + "event_id": "3e0a56a0", + "vehicle_id": "east_E_1_19", + "flow_name": "east", + "spawn_step": 1207, + "junction_id": 4, + "route_id": 97, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 54.76749651203098, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.1918418580155519 + } + }, + { + "event_id": "de5e2998", + "vehicle_id": "east_E_1_20", + "flow_name": "east", + "spawn_step": 1224, + "junction_id": 4, + "route_id": 107, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 54.055179594096536, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2089208220715388 + } + }, + { + "event_id": "8166017e", + "vehicle_id": "east_E_1_21", + "flow_name": "east", + "spawn_step": 1248, + "junction_id": 4, + "route_id": 124, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 50.2989712978552, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.2325230059269097 + } + }, + { + "event_id": "5605df10", + "vehicle_id": "east_E_2_32", + "flow_name": "east", + "spawn_step": 1272, + "junction_id": 4, + "route_id": 135, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 54.93300905644964, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.2565626168329382 + } + }, + { + "event_id": "7a429718", + "vehicle_id": "west_W_0_10", + "flow_name": "west", + "spawn_step": 1296, + "junction_id": 4, + "route_id": 181, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 62.55050716561348, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.383453298854527 + } + }, + { + "event_id": "f8b65ae1", + "vehicle_id": "east_E_0_7", + "flow_name": "east", + "spawn_step": 1298, + "junction_id": 4, + "route_id": 112, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 55.18191410115757, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.2802562825084798 + } + }, + { + "event_id": "9fd06475", + "vehicle_id": "north_N_1_19", + "flow_name": "north", + "spawn_step": 1299, + "junction_id": 4, + "route_id": 30, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 54.31618113753053, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.3707230310636647 + } + }, + { + "event_id": "0e871ce8", + "vehicle_id": "west_W_1_21", + "flow_name": "west", + "spawn_step": 1313, + "junction_id": 4, + "route_id": 172, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 53.66599871239884, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3675321927729502 + } + }, + { + "event_id": "c05a87f4", + "vehicle_id": "east_E_1_22", + "flow_name": "east", + "spawn_step": 1330, + "junction_id": 4, + "route_id": 133, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 54.75088401382482, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.3065983013693532 + } + }, + { + "event_id": "1910e612", + "vehicle_id": "north_N_1_20", + "flow_name": "north", + "spawn_step": 1367, + "junction_id": 4, + "route_id": 13, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 57.472992320171215, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3224943844335153 + } + }, + { + "event_id": "b05e9649", + "vehicle_id": "east_E_2_33", + "flow_name": "east", + "spawn_step": 1378, + "junction_id": 4, + "route_id": 99, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.677070236329115, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.3357414831130432 + } + }, + { + "event_id": "67cbc69c", + "vehicle_id": "east_E_1_23", + "flow_name": "east", + "spawn_step": 1385, + "junction_id": 4, + "route_id": 127, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 52.81941718718701, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.338520813348342 + } + }, + { + "event_id": "dd4014f4", + "vehicle_id": "south_S_0_9", + "flow_name": "south", + "spawn_step": 1386, + "junction_id": 4, + "route_id": 53, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 49.71964288074503, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.1362324102030723 + } + }, + { + "event_id": "91bf49a7", + "vehicle_id": "north_N_2_34", + "flow_name": "north", + "spawn_step": 1388, + "junction_id": 4, + "route_id": 27, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 58.313952871911724, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.3041964496784864 + } + }, + { + "event_id": "a6aef2d7", + "vehicle_id": "north_N_2_35", + "flow_name": "north", + "spawn_step": 1391, + "junction_id": 4, + "route_id": 41, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 55.81368425811989, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.30171770026962 + } + }, + { + "event_id": "fcde9748", + "vehicle_id": "east_E_1_24", + "flow_name": "east", + "spawn_step": 1408, + "junction_id": 4, + "route_id": 122, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 51.19578564559039, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3461810990005325 + } + }, + { + "event_id": "dea3f5bf", + "vehicle_id": "north_N_2_36", + "flow_name": "north", + "spawn_step": 1413, + "junction_id": 4, + "route_id": 34, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 52.703648762890786, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.2809611352851027 + } + }, + { + "event_id": "94590359", + "vehicle_id": "east_E_2_34", + "flow_name": "east", + "spawn_step": 1414, + "junction_id": 4, + "route_id": 104, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 52.431648656745594, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.3475061693952817 + } + }, + { + "event_id": "a5a977a7", + "vehicle_id": "east_E_2_35", + "flow_name": "east", + "spawn_step": 1425, + "junction_id": 4, + "route_id": 140, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 49.06071174599159, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.3491592773981091 + } + }, + { + "event_id": "862dea38", + "vehicle_id": "south_S_2_33", + "flow_name": "south", + "spawn_step": 1428, + "junction_id": 4, + "route_id": 63, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 55.31161354676087, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.1155490108389656 + } + }, + { + "event_id": "099f5782", + "vehicle_id": "north_N_2_37", + "flow_name": "north", + "spawn_step": 1430, + "junction_id": 4, + "route_id": 46, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 48.579349907545705, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.2655765650827744 + } + }, + { + "event_id": "a8f63b75", + "vehicle_id": "east_E_1_25", + "flow_name": "east", + "spawn_step": 1450, + "junction_id": 4, + "route_id": 102, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 51.27051713437996, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.3495950813213724 + } + }, + { + "event_id": "675ad1f3", + "vehicle_id": "north_N_1_21", + "flow_name": "north", + "spawn_step": 1476, + "junction_id": 4, + "route_id": 23, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 56.05574356311968, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.2222054229149897 + } + }, + { + "event_id": "3547daf9", + "vehicle_id": "west_W_1_22", + "flow_name": "west", + "spawn_step": 1479, + "junction_id": 4, + "route_id": 173, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 59.881784946414875, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.2021395295414279 + } + }, + { + "event_id": "c0d1346c", + "vehicle_id": "north_N_0_8", + "flow_name": "north", + "spawn_step": 1484, + "junction_id": 4, + "route_id": 5, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 45.97999205739117, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.2139807052980995 + } + }, + { + "event_id": "c169dcc6", + "vehicle_id": "south_S_2_34", + "flow_name": "south", + "spawn_step": 1491, + "junction_id": 4, + "route_id": 74, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 56.087573413519195, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0880993031615012 + } + }, + { + "event_id": "a67e8dc7", + "vehicle_id": "north_N_1_22", + "flow_name": "north", + "spawn_step": 1497, + "junction_id": 4, + "route_id": 47, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 59.874111247617286, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.2024679213731042 + } + }, + { + "event_id": "22bedaff", + "vehicle_id": "east_E_2_36", + "flow_name": "east", + "spawn_step": 1498, + "junction_id": 4, + "route_id": 136, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 56.32815246034955, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.3369730676524416 + } + }, + { + "event_id": "ab2401e3", + "vehicle_id": "east_E_2_37", + "flow_name": "east", + "spawn_step": 1514, + "junction_id": 4, + "route_id": 141, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 51.17727108563418, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.328994323623479 + } + }, + { + "event_id": "9c24a898", + "vehicle_id": "east_E_0_8", + "flow_name": "east", + "spawn_step": 1520, + "junction_id": 4, + "route_id": 108, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 49.11024889814024, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.3254940455766557 + } + }, + { + "event_id": "82fd85dd", + "vehicle_id": "east_E_2_38", + "flow_name": "east", + "spawn_step": 1523, + "junction_id": 4, + "route_id": 96, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 52.23802389906368, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.3241233202634701 + } + }, + { + "event_id": "61fafdf0", + "vehicle_id": "north_N_0_9", + "flow_name": "north", + "spawn_step": 1550, + "junction_id": 4, + "route_id": 29, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 44.40670639250757, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.155090560424446 + } + }, + { + "event_id": "0f8954d6", + "vehicle_id": "south_S_1_25", + "flow_name": "south", + "spawn_step": 1595, + "junction_id": 4, + "route_id": 79, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 60.831618077447374, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.052045494248798 + } + }, + { + "event_id": "654a98d1", + "vehicle_id": "east_E_0_9", + "flow_name": "east", + "spawn_step": 1608, + "junction_id": 4, + "route_id": 110, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 57.97293756068878, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.2554250830217102 + } + }, + { + "event_id": "4a910176", + "vehicle_id": "west_W_1_23", + "flow_name": "west", + "spawn_step": 1608, + "junction_id": 4, + "route_id": 170, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 42.86417798971604, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.1024293716948408 + } + }, + { + "event_id": "c74d84e3", + "vehicle_id": "east_E_1_26", + "flow_name": "east", + "spawn_step": 1613, + "junction_id": 4, + "route_id": 140, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 51.35205132827217, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.2503008382520981 + } + }, + { + "event_id": "30f3b74f", + "vehicle_id": "north_N_1_23", + "flow_name": "north", + "spawn_step": 1614, + "junction_id": 4, + "route_id": 10, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 45.886354228804606, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.1060452012727802 + } + }, + { + "event_id": "7a1310ce", + "vehicle_id": "east_E_0_10", + "flow_name": "east", + "spawn_step": 1621, + "junction_id": 4, + "route_id": 105, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 48.93747243544658, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.2429511028259492 + } + }, + { + "event_id": "136bc3b2", + "vehicle_id": "north_N_2_38", + "flow_name": "north", + "spawn_step": 1626, + "junction_id": 4, + "route_id": 15, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 49.6452259307821, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0979718011239197 + } + }, + { + "event_id": "f90987bb", + "vehicle_id": "north_N_0_10", + "flow_name": "north", + "spawn_step": 1638, + "junction_id": 4, + "route_id": 24, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 48.21438890506636, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0908690738196989 + } + }, + { + "event_id": "c81eb6dd", + "vehicle_id": "west_W_0_11", + "flow_name": "west", + "spawn_step": 1643, + "junction_id": 4, + "route_id": 172, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 54.11900214971188, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0825345433804452 + } + }, + { + "event_id": "10a9cfc6", + "vehicle_id": "east_E_0_11", + "flow_name": "east", + "spawn_step": 1654, + "junction_id": 4, + "route_id": 115, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 56.52931805015857, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.209429784424898 + } + }, + { + "event_id": "b9f7d735", + "vehicle_id": "south_S_2_35", + "flow_name": "south", + "spawn_step": 1672, + "junction_id": 4, + "route_id": 87, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 59.351762401739954, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0331124268552516 + } + }, + { + "event_id": "ba471bea", + "vehicle_id": "south_S_2_36", + "flow_name": "south", + "spawn_step": 1679, + "junction_id": 4, + "route_id": 72, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 61.54449748168492, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.031717480899407 + } + }, + { + "event_id": "bb29584d", + "vehicle_id": "west_W_1_24", + "flow_name": "west", + "spawn_step": 1679, + "junction_id": 4, + "route_id": 160, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 58.009707678507496, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0650673438041078 + } + }, + { + "event_id": "9830169f", + "vehicle_id": "north_N_0_11", + "flow_name": "north", + "spawn_step": 1680, + "junction_id": 4, + "route_id": 20, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 60.33716267176563, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.06747502599544 + } + }, + { + "event_id": "ffba4648", + "vehicle_id": "west_W_1_25", + "flow_name": "west", + "spawn_step": 1701, + "junction_id": 4, + "route_id": 169, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 64.31073522031147, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.056098721093022 + } + }, + { + "event_id": "60235bf4", + "vehicle_id": "north_N_0_12", + "flow_name": "north", + "spawn_step": 1709, + "junction_id": 4, + "route_id": 24, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 60.21775498229615, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0539211103620123 + } + }, + { + "event_id": "d6684359", + "vehicle_id": "north_N_0_13", + "flow_name": "north", + "spawn_step": 1714, + "junction_id": 4, + "route_id": 26, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 62.043481264396036, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0520392917597174 + } + }, + { + "event_id": "bfa060a4", + "vehicle_id": "north_N_1_24", + "flow_name": "north", + "spawn_step": 1725, + "junction_id": 4, + "route_id": 1, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 56.39735515877204, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0476768399323029 + } + }, + { + "event_id": "db4cb6b6", + "vehicle_id": "west_W_1_26", + "flow_name": "west", + "spawn_step": 1745, + "junction_id": 4, + "route_id": 187, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 51.4910589562636, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.040463912938672 + } + }, + { + "event_id": "b349b91d", + "vehicle_id": "west_W_2_36", + "flow_name": "west", + "spawn_step": 1756, + "junction_id": 4, + "route_id": 180, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 45.75263930935271, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0374333637917292 + } + }, + { + "event_id": "65f43907", + "vehicle_id": "south_S_2_37", + "flow_name": "south", + "spawn_step": 1763, + "junction_id": 4, + "route_id": 67, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 62.97056931020013, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0182207414885283 + } + }, + { + "event_id": "791fa2ce", + "vehicle_id": "east_E_0_12", + "flow_name": "east", + "spawn_step": 1768, + "junction_id": 4, + "route_id": 132, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 53.757829775227734, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.105439150865235 + } + }, + { + "event_id": "a82284c1", + "vehicle_id": "north_N_1_25", + "flow_name": "north", + "spawn_step": 1811, + "junction_id": 4, + "route_id": 24, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 64.56765564780014, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0223601079276516 + } + }, + { + "event_id": "efb1a4ac", + "vehicle_id": "west_W_0_12", + "flow_name": "west", + "spawn_step": 1814, + "junction_id": 4, + "route_id": 165, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 62.08310763805024, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0234442140642448 + } + }, + { + "event_id": "57508b90", + "vehicle_id": "south_S_0_10", + "flow_name": "south", + "spawn_step": 1866, + "junction_id": 4, + "route_id": 92, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 51.75857434742621, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0084600617241026 + } + }, + { + "event_id": "cfbbdf3e", + "vehicle_id": "west_W_2_37", + "flow_name": "west", + "spawn_step": 1887, + "junction_id": 4, + "route_id": 149, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 65.11751019120325, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.0123590695390348 + } + }, + { + "event_id": "ac5ff72e", + "vehicle_id": "south_S_0_11", + "flow_name": "south", + "spawn_step": 1888, + "junction_id": 4, + "route_id": 85, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 54.51990548252022, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0071285628334294 + } + }, + { + "event_id": "3d9d5966", + "vehicle_id": "west_W_2_38", + "flow_name": "west", + "spawn_step": 1974, + "junction_id": 4, + "route_id": 183, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 54.93584944011865, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "7bb6f37b", + "vehicle_id": "west_W_0_13", + "flow_name": "west", + "spawn_step": 1988, + "junction_id": 4, + "route_id": 168, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 66.90343703216968, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "2ed5a021", + "vehicle_id": "west_W_2_39", + "flow_name": "west", + "spawn_step": 2000, + "junction_id": 4, + "route_id": 181, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 47.63518221811669, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "d451d57c", + "vehicle_id": "south_S_2_38", + "flow_name": "south", + "spawn_step": 2034, + "junction_id": 4, + "route_id": 67, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 56.996950797690445, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "82cbe0ed", + "vehicle_id": "south_S_1_26", + "flow_name": "south", + "spawn_step": 2064, + "junction_id": 4, + "route_id": 84, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 54.495049591353414, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "ee670ede", + "vehicle_id": "east_E_0_13", + "flow_name": "east", + "spawn_step": 2088, + "junction_id": 4, + "route_id": 125, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 57.19707053473289, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "ae3c4bf6", + "vehicle_id": "south_S_0_12", + "flow_name": "south", + "spawn_step": 2094, + "junction_id": 4, + "route_id": 81, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 53.77081290963605, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "cfa5a932", + "vehicle_id": "north_N_2_39", + "flow_name": "north", + "spawn_step": 2105, + "junction_id": 4, + "route_id": 28, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 60.510478200194136, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "b6237668", + "vehicle_id": "south_S_0_13", + "flow_name": "south", + "spawn_step": 2115, + "junction_id": 4, + "route_id": 95, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 52.20805640373285, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "662b4a2e", + "vehicle_id": "south_S_2_39", + "flow_name": "south", + "spawn_step": 2121, + "junction_id": 4, + "route_id": 87, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 62.38523830536873, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "d1a388f8", + "vehicle_id": "north_N_1_26", + "flow_name": "north", + "spawn_step": 2134, + "junction_id": 4, + "route_id": 12, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 57.026152947496755, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "f272c27b", + "vehicle_id": "east_E_2_39", + "flow_name": "east", + "spawn_step": 2138, + "junction_id": 4, + "route_id": 110, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 54.57592683596661, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.0 + } + } + ] +} \ No newline at end of file diff --git a/recordings/traffic_500vph.json b/recordings/traffic_500vph.json new file mode 100644 index 0000000..99a45d9 --- /dev/null +++ b/recordings/traffic_500vph.json @@ -0,0 +1,8407 @@ +{ + "version": "1.0", + "timestamp": "2025-12-02T14:46:37.879738", + "total_events": 200, + "events": [ + { + "event_id": "d1637613", + "vehicle_id": "south_S_1_17", + "flow_name": "south", + "spawn_step": 241, + "junction_id": 4, + "route_id": 51, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.99312324358839, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.031922613544021 + } + }, + { + "event_id": "e9d278e6", + "vehicle_id": "west_W_0_0", + "flow_name": "west", + "spawn_step": 245, + "junction_id": 4, + "route_id": 173, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 59.046789766161986, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0096939165684546 + } + }, + { + "event_id": "b3e1f08d", + "vehicle_id": "east_E_1_17", + "flow_name": "east", + "spawn_step": 275, + "junction_id": 4, + "route_id": 106, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 54.35902761964196, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "6bafbae2", + "vehicle_id": "east_E_2_34", + "flow_name": "east", + "spawn_step": 285, + "junction_id": 4, + "route_id": 114, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 57.72228951432209, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "b44d1fe9", + "vehicle_id": "north_N_0_0", + "flow_name": "north", + "spawn_step": 290, + "junction_id": 4, + "route_id": 15, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 54.748712630673324, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "6b2e3d46", + "vehicle_id": "east_E_1_18", + "flow_name": "east", + "spawn_step": 296, + "junction_id": 4, + "route_id": 118, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 52.79069674036149, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "9305fc55", + "vehicle_id": "west_W_0_1", + "flow_name": "west", + "spawn_step": 296, + "junction_id": 4, + "route_id": 151, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 42.431832035925794, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0153434853534633 + } + }, + { + "event_id": "a826301e", + "vehicle_id": "south_S_2_34", + "flow_name": "south", + "spawn_step": 298, + "junction_id": 4, + "route_id": 85, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 60.75332426533602, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0448123690014843 + } + }, + { + "event_id": "77ea86ee", + "vehicle_id": "east_E_0_0", + "flow_name": "east", + "spawn_step": 299, + "junction_id": 4, + "route_id": 139, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 53.6801033727492, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "3e98477b", + "vehicle_id": "east_E_0_1", + "flow_name": "east", + "spawn_step": 353, + "junction_id": 4, + "route_id": 116, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 59.0962131913839, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "8e207e4f", + "vehicle_id": "south_S_0_0", + "flow_name": "south", + "spawn_step": 363, + "junction_id": 4, + "route_id": 75, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 56.348042821511214, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0640493894048486 + } + }, + { + "event_id": "beb4ddba", + "vehicle_id": "south_S_2_35", + "flow_name": "south", + "spawn_step": 403, + "junction_id": 4, + "route_id": 76, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 54.313254821495725, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.078254523607412 + } + }, + { + "event_id": "301a87cc", + "vehicle_id": "south_S_0_1", + "flow_name": "south", + "spawn_step": 414, + "junction_id": 4, + "route_id": 49, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 50.11022166281698, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0825358936005354 + } + }, + { + "event_id": "b3841167", + "vehicle_id": "north_N_2_34", + "flow_name": "north", + "spawn_step": 425, + "junction_id": 4, + "route_id": 13, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 49.82221475638016, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "62abd95f", + "vehicle_id": "north_N_0_1", + "flow_name": "north", + "spawn_step": 426, + "junction_id": 4, + "route_id": 15, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 58.20986379430095, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "15af13d5", + "vehicle_id": "north_N_2_35", + "flow_name": "north", + "spawn_step": 439, + "junction_id": 4, + "route_id": 5, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 49.27703390884324, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "c03ac625", + "vehicle_id": "south_S_2_36", + "flow_name": "south", + "spawn_step": 450, + "junction_id": 4, + "route_id": 69, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 58.75979767796037, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0973418974785258 + } + }, + { + "event_id": "c9d8807f", + "vehicle_id": "west_W_0_2", + "flow_name": "west", + "spawn_step": 461, + "junction_id": 4, + "route_id": 146, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 68.2447226087096, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0572977331765692 + } + }, + { + "event_id": "2d8041bf", + "vehicle_id": "west_W_1_17", + "flow_name": "west", + "spawn_step": 478, + "junction_id": 4, + "route_id": 147, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 47.63932644263548, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.064406710934833 + } + }, + { + "event_id": "8b11bac6", + "vehicle_id": "east_E_1_19", + "flow_name": "east", + "spawn_step": 482, + "junction_id": 4, + "route_id": 123, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 54.52519393934565, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "03799c4d", + "vehicle_id": "south_S_0_2", + "flow_name": "south", + "spawn_step": 493, + "junction_id": 4, + "route_id": 91, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 55.77848516717608, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.11677393128863 + } + }, + { + "event_id": "97af7e5f", + "vehicle_id": "south_S_1_18", + "flow_name": "south", + "spawn_step": 513, + "junction_id": 4, + "route_id": 56, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 53.29684116044025, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.1263594129426282 + } + }, + { + "event_id": "408c98cd", + "vehicle_id": "west_W_1_18", + "flow_name": "west", + "spawn_step": 521, + "junction_id": 4, + "route_id": 152, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 46.49339492063124, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.085248776745795 + } + }, + { + "event_id": "10ee9666", + "vehicle_id": "west_W_1_19", + "flow_name": "west", + "spawn_step": 524, + "junction_id": 4, + "route_id": 179, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 56.878144304168046, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0866903456450168 + } + }, + { + "event_id": "dd8d52c6", + "vehicle_id": "south_S_1_19", + "flow_name": "south", + "spawn_step": 539, + "junction_id": 4, + "route_id": 92, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 48.074572239706356, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.139263378579513 + } + }, + { + "event_id": "3c99179e", + "vehicle_id": "north_N_1_17", + "flow_name": "north", + "spawn_step": 540, + "junction_id": 4, + "route_id": 2, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 56.095643508374366, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0139306270347406 + } + }, + { + "event_id": "42606bde", + "vehicle_id": "west_W_0_3", + "flow_name": "west", + "spawn_step": 578, + "junction_id": 4, + "route_id": 151, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 47.13493643161114, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.1199120568377634 + } + }, + { + "event_id": "a98af117", + "vehicle_id": "north_N_1_18", + "flow_name": "north", + "spawn_step": 581, + "junction_id": 4, + "route_id": 44, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 57.911613643305266, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0209395502144785 + } + }, + { + "event_id": "466714d2", + "vehicle_id": "north_N_1_19", + "flow_name": "north", + "spawn_step": 582, + "junction_id": 4, + "route_id": 31, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 63.066360344719584, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0211193315617275 + } + }, + { + "event_id": "147939ff", + "vehicle_id": "west_W_1_20", + "flow_name": "west", + "spawn_step": 593, + "junction_id": 4, + "route_id": 158, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 61.72583905339805, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.130992894104545 + } + }, + { + "event_id": "f5a82199", + "vehicle_id": "north_N_1_20", + "flow_name": "north", + "spawn_step": 596, + "junction_id": 4, + "route_id": 36, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 54.195373064952236, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0241795736236265 + } + }, + { + "event_id": "d8969b1d", + "vehicle_id": "south_S_0_3", + "flow_name": "south", + "spawn_step": 622, + "junction_id": 4, + "route_id": 49, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 55.22845942145195, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.1830165394020606 + } + }, + { + "event_id": "07c950ce", + "vehicle_id": "north_N_1_21", + "flow_name": "north", + "spawn_step": 627, + "junction_id": 4, + "route_id": 28, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 47.902136100738666, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0320177803908661 + } + }, + { + "event_id": "1bb45608", + "vehicle_id": "west_W_0_4", + "flow_name": "west", + "spawn_step": 639, + "junction_id": 4, + "route_id": 178, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 51.074236266324995, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.1662613259359964 + } + }, + { + "event_id": "04e5a31d", + "vehicle_id": "south_S_1_20", + "flow_name": "south", + "spawn_step": 648, + "junction_id": 4, + "route_id": 88, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 58.757394031039624, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.197081856827028 + } + }, + { + "event_id": "5b79431a", + "vehicle_id": "west_W_0_5", + "flow_name": "west", + "spawn_step": 654, + "junction_id": 4, + "route_id": 167, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 51.831002533672475, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.1790555254174824 + } + }, + { + "event_id": "1efb46fc", + "vehicle_id": "north_N_0_2", + "flow_name": "north", + "spawn_step": 657, + "junction_id": 4, + "route_id": 31, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 61.19134859694866, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.041203754084766 + } + }, + { + "event_id": "b9764362", + "vehicle_id": "north_N_0_3", + "flow_name": "north", + "spawn_step": 670, + "junction_id": 4, + "route_id": 5, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 48.65021554345575, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.046016309159092 + } + }, + { + "event_id": "3eae7145", + "vehicle_id": "south_S_1_21", + "flow_name": "south", + "spawn_step": 693, + "junction_id": 4, + "route_id": 93, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.734955596746566, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.220215775462311 + } + }, + { + "event_id": "addb9d2e", + "vehicle_id": "east_E_1_20", + "flow_name": "east", + "spawn_step": 720, + "junction_id": 4, + "route_id": 115, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 60.49310922050198, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "0ae493b2", + "vehicle_id": "south_S_1_22", + "flow_name": "south", + "spawn_step": 723, + "junction_id": 4, + "route_id": 48, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 52.223320311409886, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2353183584699028 + } + }, + { + "event_id": "0ef6ce6a", + "vehicle_id": "west_W_2_34", + "flow_name": "west", + "spawn_step": 724, + "junction_id": 4, + "route_id": 174, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 51.677350782568595, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.244499154819929 + } + }, + { + "event_id": "f543f1c4", + "vehicle_id": "south_S_0_4", + "flow_name": "south", + "spawn_step": 726, + "junction_id": 4, + "route_id": 59, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 54.00414631750906, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.2366097201376132 + } + }, + { + "event_id": "56735b56", + "vehicle_id": "south_S_1_23", + "flow_name": "south", + "spawn_step": 744, + "junction_id": 4, + "route_id": 62, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 53.031124423788825, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2451564492414653 + } + }, + { + "event_id": "39524891", + "vehicle_id": "south_S_0_5", + "flow_name": "south", + "spawn_step": 751, + "junction_id": 4, + "route_id": 76, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 48.50784003453206, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.248616255311596 + } + }, + { + "event_id": "9118ec3d", + "vehicle_id": "east_E_0_2", + "flow_name": "east", + "spawn_step": 751, + "junction_id": 4, + "route_id": 98, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 50.25262821899707, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "e23089a6", + "vehicle_id": "south_S_0_6", + "flow_name": "south", + "spawn_step": 752, + "junction_id": 4, + "route_id": 85, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 55.79948020940355, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.2489285892682997 + } + }, + { + "event_id": "1db8e26c", + "vehicle_id": "east_E_2_35", + "flow_name": "east", + "spawn_step": 765, + "junction_id": 4, + "route_id": 107, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.95375777521461, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "306b68f3", + "vehicle_id": "east_E_2_36", + "flow_name": "east", + "spawn_step": 765, + "junction_id": 4, + "route_id": 130, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 55.33937611073064, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "059c71a7", + "vehicle_id": "south_S_1_24", + "flow_name": "south", + "spawn_step": 766, + "junction_id": 4, + "route_id": 55, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 53.57096430080234, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.2550754973864238 + } + }, + { + "event_id": "0b49f451", + "vehicle_id": "west_W_0_6", + "flow_name": "west", + "spawn_step": 792, + "junction_id": 4, + "route_id": 191, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 63.54612639812229, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.3129249597885673 + } + }, + { + "event_id": "719a26e2", + "vehicle_id": "west_W_1_21", + "flow_name": "west", + "spawn_step": 801, + "junction_id": 4, + "route_id": 182, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 47.30256154461143, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.321738075730839 + } + }, + { + "event_id": "27be7097", + "vehicle_id": "west_W_1_22", + "flow_name": "west", + "spawn_step": 802, + "junction_id": 4, + "route_id": 153, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 54.29496029132504, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.3227760295110293 + } + }, + { + "event_id": "3a9e9f77", + "vehicle_id": "west_W_1_23", + "flow_name": "west", + "spawn_step": 813, + "junction_id": 4, + "route_id": 155, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 57.444320975225736, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.3339718574626072 + } + }, + { + "event_id": "021abd2c", + "vehicle_id": "north_N_0_4", + "flow_name": "north", + "spawn_step": 818, + "junction_id": 4, + "route_id": 44, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.0770384268248, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.1299840965521646 + } + }, + { + "event_id": "43f9b265", + "vehicle_id": "west_W_1_24", + "flow_name": "west", + "spawn_step": 846, + "junction_id": 4, + "route_id": 145, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 40.13020763477179, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.366898839474188 + } + }, + { + "event_id": "97303e0b", + "vehicle_id": "west_W_2_35", + "flow_name": "west", + "spawn_step": 865, + "junction_id": 4, + "route_id": 150, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 58.49892746032621, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.3849554260051304 + } + }, + { + "event_id": "2f2e9a4d", + "vehicle_id": "south_S_2_37", + "flow_name": "south", + "spawn_step": 869, + "junction_id": 4, + "route_id": 72, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 60.34333962524058, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.2894533154875762 + } + }, + { + "event_id": "6e4c9bcb", + "vehicle_id": "south_S_2_38", + "flow_name": "south", + "spawn_step": 880, + "junction_id": 4, + "route_id": 60, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 54.5448020254801, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.291869530825085 + } + }, + { + "event_id": "d5499c4c", + "vehicle_id": "west_W_1_25", + "flow_name": "west", + "spawn_step": 902, + "junction_id": 4, + "route_id": 159, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 48.05620368204263, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.418216579446453 + } + }, + { + "event_id": "df2384b2", + "vehicle_id": "west_W_0_7", + "flow_name": "west", + "spawn_step": 905, + "junction_id": 4, + "route_id": 185, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 61.47566111914749, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.4208543867592747 + } + }, + { + "event_id": "02552e5c", + "vehicle_id": "north_N_2_36", + "flow_name": "north", + "spawn_step": 907, + "junction_id": 4, + "route_id": 35, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 46.46507359440151, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.2062747179387034 + } + }, + { + "event_id": "009fa736", + "vehicle_id": "east_E_2_37", + "flow_name": "east", + "spawn_step": 911, + "junction_id": 4, + "route_id": 113, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 52.24411275179742, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0157774566833437 + } + }, + { + "event_id": "082acc24", + "vehicle_id": "west_W_1_26", + "flow_name": "west", + "spawn_step": 912, + "junction_id": 4, + "route_id": 155, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 40.87200197213055, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.426078604352143 + } + }, + { + "event_id": "aeb1d098", + "vehicle_id": "south_S_0_7", + "flow_name": "south", + "spawn_step": 913, + "junction_id": 4, + "route_id": 79, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 52.88509261808853, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.297233978661376 + } + }, + { + "event_id": "c2f949f5", + "vehicle_id": "south_S_0_8", + "flow_name": "south", + "spawn_step": 914, + "junction_id": 4, + "route_id": 74, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 50.499853037227915, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.2973455668502791 + } + }, + { + "event_id": "d7736b3f", + "vehicle_id": "north_N_1_22", + "flow_name": "north", + "spawn_step": 917, + "junction_id": 4, + "route_id": 3, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 63.305208618917206, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.2160922809009875 + } + }, + { + "event_id": "1ec916a2", + "vehicle_id": "north_N_2_37", + "flow_name": "north", + "spawn_step": 917, + "junction_id": 4, + "route_id": 26, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 57.88575996641059, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.216408317343185 + } + }, + { + "event_id": "2dec8233", + "vehicle_id": "east_E_1_21", + "flow_name": "east", + "spawn_step": 928, + "junction_id": 4, + "route_id": 126, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 52.009954200868314, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0190577481140612 + } + }, + { + "event_id": "b5e78c66", + "vehicle_id": "west_W_0_8", + "flow_name": "west", + "spawn_step": 930, + "junction_id": 4, + "route_id": 183, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 64.11742132629362, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.4407877530077182 + } + }, + { + "event_id": "1b5a1a15", + "vehicle_id": "west_W_0_9", + "flow_name": "west", + "spawn_step": 932, + "junction_id": 4, + "route_id": 161, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 57.57029255771731, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.4420231682314144 + } + }, + { + "event_id": "e5b0082d", + "vehicle_id": "south_S_2_39", + "flow_name": "south", + "spawn_step": 938, + "junction_id": 4, + "route_id": 62, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 46.04903321072362, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.29937683222516 + } + }, + { + "event_id": "4f198cc1", + "vehicle_id": "north_N_2_38", + "flow_name": "north", + "spawn_step": 939, + "junction_id": 4, + "route_id": 30, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 43.65638750949968, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.237418374048742 + } + }, + { + "event_id": "7b7eb262", + "vehicle_id": "north_N_1_23", + "flow_name": "north", + "spawn_step": 948, + "junction_id": 4, + "route_id": 30, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 60.55941148295582, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.245968387094671 + } + }, + { + "event_id": "f77fef07", + "vehicle_id": "south_S_2_40", + "flow_name": "south", + "spawn_step": 949, + "junction_id": 4, + "route_id": 95, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 56.635519993926984, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.299854801776773 + } + }, + { + "event_id": "c8bb39c5", + "vehicle_id": "west_W_1_27", + "flow_name": "west", + "spawn_step": 961, + "junction_id": 4, + "route_id": 191, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 53.14591053410217, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.461515372840346 + } + }, + { + "event_id": "8d6d01a0", + "vehicle_id": "west_W_2_36", + "flow_name": "west", + "spawn_step": 967, + "junction_id": 4, + "route_id": 172, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 62.90863019211474, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.465476429325891 + } + }, + { + "event_id": "d9b63e35", + "vehicle_id": "south_S_1_25", + "flow_name": "south", + "spawn_step": 976, + "junction_id": 4, + "route_id": 93, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 53.493968286576724, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.2996424633685355 + } + }, + { + "event_id": "56bf2dac", + "vehicle_id": "north_N_1_24", + "flow_name": "north", + "spawn_step": 980, + "junction_id": 4, + "route_id": 25, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 44.3713241026249, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.2758029685364765 + } + }, + { + "event_id": "5bc00a3f", + "vehicle_id": "south_S_1_26", + "flow_name": "south", + "spawn_step": 996, + "junction_id": 4, + "route_id": 65, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 58.71776844185539, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.2982631887984502 + } + }, + { + "event_id": "24257036", + "vehicle_id": "south_S_0_9", + "flow_name": "south", + "spawn_step": 1016, + "junction_id": 4, + "route_id": 54, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.64720600022922, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.295938369492523 + } + }, + { + "event_id": "0a99dfcf", + "vehicle_id": "west_W_2_37", + "flow_name": "west", + "spawn_step": 1019, + "junction_id": 4, + "route_id": 164, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 43.108741650317114, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.4898474989551342 + } + }, + { + "event_id": "cfab579d", + "vehicle_id": "south_S_2_41", + "flow_name": "south", + "spawn_step": 1022, + "junction_id": 4, + "route_id": 72, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 61.19866547073336, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.2949889065216214 + } + }, + { + "event_id": "c9b474f0", + "vehicle_id": "west_W_1_28", + "flow_name": "west", + "spawn_step": 1023, + "junction_id": 4, + "route_id": 191, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 51.678739356270604, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.4909261369189153 + } + }, + { + "event_id": "fd0da34b", + "vehicle_id": "north_N_1_25", + "flow_name": "north", + "spawn_step": 1031, + "junction_id": 4, + "route_id": 40, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 45.81578964462094, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.3215608382946602 + } + }, + { + "event_id": "6838472d", + "vehicle_id": "east_E_1_22", + "flow_name": "east", + "spawn_step": 1031, + "junction_id": 4, + "route_id": 132, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 55.51580132321797, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0550185921751196 + } + }, + { + "event_id": "5c00664b", + "vehicle_id": "east_E_0_3", + "flow_name": "east", + "spawn_step": 1042, + "junction_id": 4, + "route_id": 136, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 60.104913028973044, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.060651877423338 + } + }, + { + "event_id": "1f18393b", + "vehicle_id": "east_E_2_38", + "flow_name": "east", + "spawn_step": 1044, + "junction_id": 4, + "route_id": 126, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 57.35559577760466, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.0617819753078404 + } + }, + { + "event_id": "9973bb16", + "vehicle_id": "east_E_1_23", + "flow_name": "east", + "spawn_step": 1049, + "junction_id": 4, + "route_id": 140, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 49.420493447182416, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0645021138967308 + } + }, + { + "event_id": "b8da0053", + "vehicle_id": "north_N_1_26", + "flow_name": "north", + "spawn_step": 1056, + "junction_id": 4, + "route_id": 36, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 44.307118933899815, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.3415576872421464 + } + }, + { + "event_id": "c5fead70", + "vehicle_id": "west_W_2_38", + "flow_name": "west", + "spawn_step": 1060, + "junction_id": 4, + "route_id": 152, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 55.50424912842589, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.4988951163079727 + } + }, + { + "event_id": "3076e286", + "vehicle_id": "west_W_2_39", + "flow_name": "west", + "spawn_step": 1061, + "junction_id": 4, + "route_id": 181, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 57.79821369405715, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.4990594291959118 + } + }, + { + "event_id": "8a8e9a62", + "vehicle_id": "south_S_2_42", + "flow_name": "south", + "spawn_step": 1062, + "junction_id": 4, + "route_id": 89, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 53.348265036271705, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.2867031841085872 + } + }, + { + "event_id": "82d9dd40", + "vehicle_id": "west_W_0_10", + "flow_name": "west", + "spawn_step": 1063, + "junction_id": 4, + "route_id": 157, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 50.80924172642568, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.4992403785868575 + } + }, + { + "event_id": "516f9312", + "vehicle_id": "west_W_1_29", + "flow_name": "west", + "spawn_step": 1071, + "junction_id": 4, + "route_id": 167, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 54.69817456987962, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.4998002888769186 + } + }, + { + "event_id": "b27e2f96", + "vehicle_id": "west_W_2_40", + "flow_name": "west", + "spawn_step": 1076, + "junction_id": 4, + "route_id": 168, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 53.04845679625708, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.4999714510261355 + } + }, + { + "event_id": "e1894640", + "vehicle_id": "north_N_2_39", + "flow_name": "north", + "spawn_step": 1087, + "junction_id": 4, + "route_id": 18, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 57.30710566735216, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.363002479884951 + } + }, + { + "event_id": "3c08d98b", + "vehicle_id": "west_W_0_11", + "flow_name": "west", + "spawn_step": 1090, + "junction_id": 4, + "route_id": 159, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 41.78464300363911, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.4996581721650089 + } + }, + { + "event_id": "011d124e", + "vehicle_id": "south_S_2_43", + "flow_name": "south", + "spawn_step": 1091, + "junction_id": 4, + "route_id": 50, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 53.50526765802846, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.278451574812198 + } + }, + { + "event_id": "ceb09b8e", + "vehicle_id": "west_W_2_41", + "flow_name": "west", + "spawn_step": 1109, + "junction_id": 4, + "route_id": 173, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 55.01874587649135, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.497500666944975 + } + }, + { + "event_id": "0bad34a7", + "vehicle_id": "east_E_0_4", + "flow_name": "east", + "spawn_step": 1116, + "junction_id": 4, + "route_id": 143, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 53.67887212503382, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.1095952952275254 + } + }, + { + "event_id": "97cdc477", + "vehicle_id": "north_N_2_40", + "flow_name": "north", + "spawn_step": 1118, + "junction_id": 4, + "route_id": 29, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 56.87582526802695, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.379998169961525 + } + }, + { + "event_id": "2eb51894", + "vehicle_id": "north_N_0_5", + "flow_name": "north", + "spawn_step": 1121, + "junction_id": 4, + "route_id": 37, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 52.337262895579684, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.3812537900251516 + } + }, + { + "event_id": "91982d80", + "vehicle_id": "north_N_1_27", + "flow_name": "north", + "spawn_step": 1129, + "junction_id": 4, + "route_id": 15, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 54.111446628031366, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3849062279306035 + } + }, + { + "event_id": "fa22c7a0", + "vehicle_id": "north_N_0_6", + "flow_name": "north", + "spawn_step": 1141, + "junction_id": 4, + "route_id": 33, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 51.28261429818472, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.3895103753932414 + } + }, + { + "event_id": "0866e81f", + "vehicle_id": "south_S_2_44", + "flow_name": "south", + "spawn_step": 1147, + "junction_id": 4, + "route_id": 85, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 59.52759686855746, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.2573579185046153 + } + }, + { + "event_id": "67dd4d76", + "vehicle_id": "east_E_0_5", + "flow_name": "east", + "spawn_step": 1148, + "junction_id": 4, + "route_id": 102, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 53.64023407295897, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.1361205734453446 + } + }, + { + "event_id": "a5371835", + "vehicle_id": "south_S_1_27", + "flow_name": "south", + "spawn_step": 1151, + "junction_id": 4, + "route_id": 78, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 52.89902035364505, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.2556702414189331 + } + }, + { + "event_id": "9bcd0559", + "vehicle_id": "north_N_2_41", + "flow_name": "north", + "spawn_step": 1152, + "junction_id": 4, + "route_id": 16, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 56.24410453451471, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.3931816603899352 + } + }, + { + "event_id": "96e3e155", + "vehicle_id": "east_E_0_6", + "flow_name": "east", + "spawn_step": 1152, + "junction_id": 4, + "route_id": 140, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 58.60896278002241, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.1394236982490589 + } + }, + { + "event_id": "61c8605f", + "vehicle_id": "west_W_2_42", + "flow_name": "west", + "spawn_step": 1162, + "junction_id": 4, + "route_id": 150, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 61.589004476531485, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.4810987925894437 + } + }, + { + "event_id": "8947bb6f", + "vehicle_id": "west_W_0_12", + "flow_name": "west", + "spawn_step": 1181, + "junction_id": 4, + "route_id": 187, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 53.569920524620535, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.471853080618368 + } + }, + { + "event_id": "3cb89f53", + "vehicle_id": "east_E_2_39", + "flow_name": "east", + "spawn_step": 1194, + "junction_id": 4, + "route_id": 117, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 53.67504350705788, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.1786957675958547 + } + }, + { + "event_id": "5f5dbadb", + "vehicle_id": "north_N_2_42", + "flow_name": "north", + "spawn_step": 1207, + "junction_id": 4, + "route_id": 12, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 44.664944220316656, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.3998132808445478 + } + }, + { + "event_id": "5472be45", + "vehicle_id": "north_N_2_43", + "flow_name": "north", + "spawn_step": 1210, + "junction_id": 4, + "route_id": 14, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 61.42772457137929, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.3996757291850455 + } + }, + { + "event_id": "ec6b5c5a", + "vehicle_id": "east_E_2_40", + "flow_name": "east", + "spawn_step": 1212, + "junction_id": 4, + "route_id": 100, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 56.65509148106229, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.1973461010469775 + } + }, + { + "event_id": "936d90be", + "vehicle_id": "east_E_2_41", + "flow_name": "east", + "spawn_step": 1216, + "junction_id": 4, + "route_id": 110, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 58.930003790315155, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.2011912353648198 + } + }, + { + "event_id": "b70d2363", + "vehicle_id": "west_W_2_43", + "flow_name": "west", + "spawn_step": 1217, + "junction_id": 4, + "route_id": 185, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 58.32803602207915, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.4494715003855922 + } + }, + { + "event_id": "40b835b8", + "vehicle_id": "south_S_1_28", + "flow_name": "south", + "spawn_step": 1221, + "junction_id": 4, + "route_id": 57, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 48.78217681742275, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.223046941956894 + } + }, + { + "event_id": "8fd1f4ee", + "vehicle_id": "east_E_0_7", + "flow_name": "east", + "spawn_step": 1241, + "junction_id": 4, + "route_id": 98, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 56.94337257120866, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.2255853380896102 + } + }, + { + "event_id": "521d7dcb", + "vehicle_id": "west_W_2_44", + "flow_name": "west", + "spawn_step": 1248, + "junction_id": 4, + "route_id": 157, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 51.38525221460382, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.4253033788630782 + } + }, + { + "event_id": "ff88ba28", + "vehicle_id": "east_E_1_24", + "flow_name": "east", + "spawn_step": 1263, + "junction_id": 4, + "route_id": 138, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 51.21790898955061, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.2477899713752945 + } + }, + { + "event_id": "1c87c863", + "vehicle_id": "east_E_0_8", + "flow_name": "east", + "spawn_step": 1273, + "junction_id": 4, + "route_id": 114, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 52.24277916808982, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.257439220538082 + } + }, + { + "event_id": "3601b5bc", + "vehicle_id": "south_S_1_29", + "flow_name": "south", + "spawn_step": 1275, + "junction_id": 4, + "route_id": 86, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 57.91465653117638, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.1945641736753756 + } + }, + { + "event_id": "aaacc475", + "vehicle_id": "east_E_0_9", + "flow_name": "east", + "spawn_step": 1289, + "junction_id": 4, + "route_id": 105, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 53.83567600051008, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.272330401743588 + } + }, + { + "event_id": "e1038014", + "vehicle_id": "north_N_1_28", + "flow_name": "north", + "spawn_step": 1297, + "junction_id": 4, + "route_id": 26, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 54.991214779969184, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.3718083874210651 + } + }, + { + "event_id": "6730ac11", + "vehicle_id": "north_N_0_7", + "flow_name": "north", + "spawn_step": 1302, + "junction_id": 4, + "route_id": 47, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 49.38522638201499, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.3690089281485025 + } + }, + { + "event_id": "5c089815", + "vehicle_id": "west_W_2_45", + "flow_name": "west", + "spawn_step": 1305, + "junction_id": 4, + "route_id": 158, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 48.6767938956225, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.3744912837526053 + } + }, + { + "event_id": "c335ac63", + "vehicle_id": "north_N_2_44", + "flow_name": "north", + "spawn_step": 1308, + "junction_id": 4, + "route_id": 9, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 43.65599260118481, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.3654755739399707 + } + }, + { + "event_id": "da98b285", + "vehicle_id": "west_W_0_13", + "flow_name": "west", + "spawn_step": 1325, + "junction_id": 4, + "route_id": 185, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 41.39097931795267, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.355154050475769 + } + }, + { + "event_id": "6b7e7d3b", + "vehicle_id": "north_N_0_8", + "flow_name": "north", + "spawn_step": 1336, + "junction_id": 4, + "route_id": 44, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 48.230805104570976, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.3461470581100163 + } + }, + { + "event_id": "505983a1", + "vehicle_id": "east_E_0_10", + "flow_name": "east", + "spawn_step": 1338, + "junction_id": 4, + "route_id": 132, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 49.766220333885094, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.3121676894140297 + } + }, + { + "event_id": "3cde4ef4", + "vehicle_id": "south_S_0_10", + "flow_name": "south", + "spawn_step": 1341, + "junction_id": 4, + "route_id": 92, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 50.68373738140405, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.1595664051691592 + } + }, + { + "event_id": "c178f2f6", + "vehicle_id": "south_S_1_30", + "flow_name": "south", + "spawn_step": 1344, + "junction_id": 4, + "route_id": 56, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 50.49669910572686, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 1, + "peak_density": 1.1581394210992066 + } + }, + { + "event_id": "640c5bc7", + "vehicle_id": "east_E_1_25", + "flow_name": "east", + "spawn_step": 1346, + "junction_id": 4, + "route_id": 132, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 57.62600792597035, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.3176882819814857 + } + }, + { + "event_id": "d20e7ed0", + "vehicle_id": "west_W_0_14", + "flow_name": "west", + "spawn_step": 1357, + "junction_id": 4, + "route_id": 161, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 65.68023221133338, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.3234526096763126 + } + }, + { + "event_id": "1ef183a5", + "vehicle_id": "south_S_2_45", + "flow_name": "south", + "spawn_step": 1359, + "junction_id": 4, + "route_id": 76, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 54.59551047429174, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.1502272424882571 + } + }, + { + "event_id": "1c376e1a", + "vehicle_id": "east_E_1_26", + "flow_name": "east", + "spawn_step": 1377, + "junction_id": 4, + "route_id": 143, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 56.696362128124385, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.3349513535159212 + } + }, + { + "event_id": "9b3d16e6", + "vehicle_id": "west_W_2_46", + "flow_name": "west", + "spawn_step": 1386, + "junction_id": 4, + "route_id": 173, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 51.14198973673298, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.2937434059439348 + } + }, + { + "event_id": "6fcb96c2", + "vehicle_id": "east_E_2_42", + "flow_name": "east", + "spawn_step": 1403, + "junction_id": 4, + "route_id": 131, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 48.75142880365895, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.3449768061090377 + } + }, + { + "event_id": "c3342dc8", + "vehicle_id": "east_E_1_27", + "flow_name": "east", + "spawn_step": 1416, + "junction_id": 4, + "route_id": 114, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 54.22377172028912, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3478146557260555 + } + }, + { + "event_id": "31f24ed1", + "vehicle_id": "east_E_0_11", + "flow_name": "east", + "spawn_step": 1423, + "junction_id": 4, + "route_id": 101, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 57.01804196798383, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.3488863044175268 + } + }, + { + "event_id": "e40780ce", + "vehicle_id": "east_E_0_12", + "flow_name": "east", + "spawn_step": 1448, + "junction_id": 4, + "route_id": 135, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 53.051937627704575, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.3496908114175274 + } + }, + { + "event_id": "420df6d1", + "vehicle_id": "north_N_0_9", + "flow_name": "north", + "spawn_step": 1457, + "junction_id": 4, + "route_id": 16, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 45.53676064533024, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.2395632809248514 + } + }, + { + "event_id": "f516653b", + "vehicle_id": "west_W_0_15", + "flow_name": "west", + "spawn_step": 1477, + "junction_id": 4, + "route_id": 173, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 58.263920759867474, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.2038161068942281 + } + }, + { + "event_id": "17e592c0", + "vehicle_id": "north_N_1_29", + "flow_name": "north", + "spawn_step": 1480, + "junction_id": 4, + "route_id": 39, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 59.73581555883544, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.2177084582664648 + } + }, + { + "event_id": "6bb9523f", + "vehicle_id": "north_N_1_30", + "flow_name": "north", + "spawn_step": 1495, + "junction_id": 4, + "route_id": 24, + "lane_id": 1, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 62.68080953031384, + "spawn_transform": { + "location": { + "x": 5.25, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.2041484694238542 + } + }, + { + "event_id": "0952ff5c", + "vehicle_id": "north_N_2_45", + "flow_name": "north", + "spawn_step": 1496, + "junction_id": 4, + "route_id": 19, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 59.24776804220733, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.2032674277459448 + } + }, + { + "event_id": "c8e1657b", + "vehicle_id": "east_E_0_13", + "flow_name": "east", + "spawn_step": 1510, + "junction_id": 4, + "route_id": 133, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.3308057376991, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.3310532786320695 + } + }, + { + "event_id": "d87914f0", + "vehicle_id": "south_S_0_11", + "flow_name": "south", + "spawn_step": 1522, + "junction_id": 4, + "route_id": 73, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 46.996213997774525, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0758705181646573 + } + }, + { + "event_id": "e3b97da6", + "vehicle_id": "south_S_1_31", + "flow_name": "south", + "spawn_step": 1523, + "junction_id": 4, + "route_id": 74, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 61.6952510158011, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0755805896442867 + } + }, + { + "event_id": "b0d4e993", + "vehicle_id": "east_E_0_14", + "flow_name": "east", + "spawn_step": 1537, + "junction_id": 4, + "route_id": 136, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 52.836793644011735, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.3149982896186767 + } + }, + { + "event_id": "0ea35610", + "vehicle_id": "north_N_0_10", + "flow_name": "north", + "spawn_step": 1538, + "junction_id": 4, + "route_id": 12, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 45.86011423096032, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.1651483385255381 + } + }, + { + "event_id": "f10b1cd2", + "vehicle_id": "east_E_2_43", + "flow_name": "east", + "spawn_step": 1538, + "junction_id": 4, + "route_id": 121, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 50.76195331539092, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 2, + "peak_density": 1.3142761075956333 + } + }, + { + "event_id": "1edfc1c7", + "vehicle_id": "east_E_2_44", + "flow_name": "east", + "spawn_step": 1545, + "junction_id": 4, + "route_id": 103, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 51.89761821259194, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.3093719143991462 + } + }, + { + "event_id": "d87856ac", + "vehicle_id": "east_E_1_28", + "flow_name": "east", + "spawn_step": 1557, + "junction_id": 4, + "route_id": 118, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 54.805868539114634, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.3003263943233434 + } + }, + { + "event_id": "3183c5ea", + "vehicle_id": "north_N_0_11", + "flow_name": "north", + "spawn_step": 1570, + "junction_id": 4, + "route_id": 34, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 47.54710424429112, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.1382997729892643 + } + }, + { + "event_id": "9b929215", + "vehicle_id": "north_N_0_12", + "flow_name": "north", + "spawn_step": 1585, + "junction_id": 4, + "route_id": 47, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 49.44580314512233, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.127226554439571 + } + }, + { + "event_id": "350abf4e", + "vehicle_id": "north_N_1_31", + "flow_name": "north", + "spawn_step": 1604, + "junction_id": 4, + "route_id": 19, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 65.0359993053321, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.1132077590904816 + } + }, + { + "event_id": "71922877", + "vehicle_id": "north_N_2_46", + "flow_name": "north", + "spawn_step": 1623, + "junction_id": 4, + "route_id": 36, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 53.1248339300945, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.099986616448173 + } + }, + { + "event_id": "81728dd4", + "vehicle_id": "west_W_1_30", + "flow_name": "west", + "spawn_step": 1624, + "junction_id": 4, + "route_id": 172, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 46.90297740592272, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "west", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.0928636630910962 + } + }, + { + "event_id": "e4d85d6f", + "vehicle_id": "east_E_1_29", + "flow_name": "east", + "spawn_step": 1649, + "junction_id": 4, + "route_id": 135, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 55.73167505054916, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.2148582575087838 + } + }, + { + "event_id": "d42f238c", + "vehicle_id": "north_N_0_13", + "flow_name": "north", + "spawn_step": 1653, + "junction_id": 4, + "route_id": 37, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 56.66305991072042, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0816280953106239 + } + }, + { + "event_id": "59b5e784", + "vehicle_id": "east_E_0_15", + "flow_name": "east", + "spawn_step": 1665, + "junction_id": 4, + "route_id": 138, + "lane_id": 0, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 58.206413645253924, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.1989594406000308 + } + }, + { + "event_id": "c9d9d9d5", + "vehicle_id": "west_W_2_47", + "flow_name": "west", + "spawn_step": 1676, + "junction_id": 4, + "route_id": 150, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 52.63260189210606, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.0666984132079198 + } + }, + { + "event_id": "bd82a3e4", + "vehicle_id": "south_S_0_12", + "flow_name": "south", + "spawn_step": 1685, + "junction_id": 4, + "route_id": 53, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 50.75907744915234, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0305420545973363 + } + }, + { + "event_id": "4ed84c13", + "vehicle_id": "east_E_2_45", + "flow_name": "east", + "spawn_step": 1690, + "junction_id": 4, + "route_id": 143, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 54.95501643936576, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.1743503954271757 + } + }, + { + "event_id": "28af44d0", + "vehicle_id": "west_W_1_31", + "flow_name": "west", + "spawn_step": 1699, + "junction_id": 4, + "route_id": 183, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 60.902029251602784, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0566788251977104 + } + }, + { + "event_id": "865bbe72", + "vehicle_id": "north_N_0_14", + "flow_name": "north", + "spawn_step": 1703, + "junction_id": 4, + "route_id": 6, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.23113344217324, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 5.25, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "north", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0566712902675026 + } + }, + { + "event_id": "3b2e6940", + "vehicle_id": "south_S_0_13", + "flow_name": "south", + "spawn_step": 1720, + "junction_id": 4, + "route_id": 55, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 60.87204598388271, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0244143152069132 + } + }, + { + "event_id": "af4fca57", + "vehicle_id": "east_E_1_30", + "flow_name": "east", + "spawn_step": 1725, + "junction_id": 4, + "route_id": 142, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 55.61093126139579, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.141781163148463 + } + }, + { + "event_id": "85f2773b", + "vehicle_id": "north_N_1_32", + "flow_name": "north", + "spawn_step": 1726, + "junction_id": 4, + "route_id": 19, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 57.39164838561716, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0469893263523293 + } + }, + { + "event_id": "c1536787", + "vehicle_id": "east_E_1_31", + "flow_name": "east", + "spawn_step": 1735, + "junction_id": 4, + "route_id": 136, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 52.19682844950916, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.1326004392425075 + } + }, + { + "event_id": "4d0af065", + "vehicle_id": "east_E_2_46", + "flow_name": "east", + "spawn_step": 1739, + "junction_id": 4, + "route_id": 136, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 50.03612382808059, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -8.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.1293469957590125 + } + }, + { + "event_id": "cfb86bb7", + "vehicle_id": "south_S_2_46", + "flow_name": "south", + "spawn_step": 1753, + "junction_id": 4, + "route_id": 77, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 58.5561553195405, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.019459213659631 + } + }, + { + "event_id": "43252246", + "vehicle_id": "south_S_1_32", + "flow_name": "south", + "spawn_step": 1756, + "junction_id": 4, + "route_id": 80, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 60.173621756791405, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0191485053329072 + } + }, + { + "event_id": "b1397d94", + "vehicle_id": "east_E_2_47", + "flow_name": "east", + "spawn_step": 1773, + "junction_id": 4, + "route_id": 110, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 49.267444175706466, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.1020239485816476 + } + }, + { + "event_id": "58023315", + "vehicle_id": "north_N_0_15", + "flow_name": "north", + "spawn_step": 1775, + "junction_id": 4, + "route_id": 23, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.492240462258735, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0311247468442748 + } + }, + { + "event_id": "73d47a26", + "vehicle_id": "north_N_2_47", + "flow_name": "north", + "spawn_step": 1775, + "junction_id": 4, + "route_id": 29, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 59.20952425826068, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.031083337157646 + } + }, + { + "event_id": "72fdd1c1", + "vehicle_id": "west_W_1_32", + "flow_name": "west", + "spawn_step": 1805, + "junction_id": 4, + "route_id": 186, + "lane_id": 1, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 49.41967083075611, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0252626171669834 + } + }, + { + "event_id": "f1e41d4d", + "vehicle_id": "south_S_2_47", + "flow_name": "south", + "spawn_step": 1828, + "junction_id": 4, + "route_id": 74, + "lane_id": 2, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 47.93001948479696, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0113883487518471 + } + }, + { + "event_id": "1a841365", + "vehicle_id": "south_S_0_14", + "flow_name": "south", + "spawn_step": 1858, + "junction_id": 4, + "route_id": 48, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 54.323603591248904, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0090130124770342 + } + }, + { + "event_id": "7b606bbb", + "vehicle_id": "south_S_2_48", + "flow_name": "south", + "spawn_step": 1859, + "junction_id": 4, + "route_id": 64, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 56.9827108648919, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0089542098776918 + } + }, + { + "event_id": "72dc02fb", + "vehicle_id": "north_N_2_48", + "flow_name": "north", + "spawn_step": 1860, + "junction_id": 4, + "route_id": 17, + "lane_id": 2, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 55.80247894917369, + "spawn_transform": { + "location": { + "x": 1.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0138002841363025 + } + }, + { + "event_id": "505b5b77", + "vehicle_id": "east_E_2_48", + "flow_name": "east", + "spawn_step": 1885, + "junction_id": 4, + "route_id": 131, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 59.33910766365859, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -5.25, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 2, + "peak_density": 1.0383944471575552 + } + }, + { + "event_id": "4e039ea9", + "vehicle_id": "north_N_1_33", + "flow_name": "north", + "spawn_step": 1919, + "junction_id": 4, + "route_id": 39, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 64.79904277024379, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0073930403013835 + } + }, + { + "event_id": "de0c2208", + "vehicle_id": "south_S_1_33", + "flow_name": "south", + "spawn_step": 1927, + "junction_id": 4, + "route_id": 66, + "lane_id": 1, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 60.56835272300595, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "059a69d3", + "vehicle_id": "south_S_0_15", + "flow_name": "south", + "spawn_step": 1931, + "junction_id": 4, + "route_id": 51, + "lane_id": 0, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 55.304249975602026, + "spawn_transform": { + "location": { + "x": -8.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -8.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "south", + "exit_direction": "north", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "642ff6b6", + "vehicle_id": "south_S_2_49", + "flow_name": "south", + "spawn_step": 1950, + "junction_id": 4, + "route_id": 73, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 60.8041273237864, + "spawn_transform": { + "location": { + "x": -5.25, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "south", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "4e51e9d4", + "vehicle_id": "east_E_2_49", + "flow_name": "east", + "spawn_step": 1978, + "junction_id": 4, + "route_id": 110, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 56.41104410795878, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -5.25, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "east", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.013878529125469 + } + }, + { + "event_id": "05f374c8", + "vehicle_id": "north_N_0_16", + "flow_name": "north", + "spawn_step": 2002, + "junction_id": 4, + "route_id": 45, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 46.7848251166556, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "left", + "entry_direction": "north", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "e3ddfb52", + "vehicle_id": "east_E_1_32", + "flow_name": "east", + "spawn_step": 2004, + "junction_id": 4, + "route_id": 124, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 55.81861406254445, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 1.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 1, + "peak_density": 1.010147200010445 + } + }, + { + "event_id": "a062857e", + "vehicle_id": "south_S_0_16", + "flow_name": "south", + "spawn_step": 2022, + "junction_id": 4, + "route_id": 87, + "lane_id": 0, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 57.06924424539265, + "spawn_transform": { + "location": { + "x": -1.75, + "y": -56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "south", + "exit_direction": "west", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "37b3f14a", + "vehicle_id": "east_E_0_16", + "flow_name": "east", + "spawn_step": 2025, + "junction_id": 4, + "route_id": 125, + "lane_id": 0, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 56.90931110056343, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 8.75, + "y": -56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "east", + "exit_direction": "south", + "lane_id": 0, + "peak_density": 1.0077973207592201 + } + }, + { + "event_id": "7533c640", + "vehicle_id": "west_W_0_16", + "flow_name": "west", + "spawn_step": 2043, + "junction_id": 4, + "route_id": 185, + "lane_id": 0, + "blueprint_id": "vehicle.lincoln.mkz_2017", + "target_speed": 67.95506608178712, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 0, + "peak_density": 1.0 + } + }, + { + "event_id": "a00eeba7", + "vehicle_id": "east_E_1_33", + "flow_name": "east", + "spawn_step": 2060, + "junction_id": 4, + "route_id": 142, + "lane_id": 1, + "blueprint_id": "vehicle.bmw.grandtourer", + "target_speed": 50.4914671332084, + "spawn_transform": { + "location": { + "x": 56.0, + "y": -1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -56.0, + "y": -8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 360.0, + "yaw": 180.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "east", + "exit_direction": "west", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "520e5531", + "vehicle_id": "west_W_2_48", + "flow_name": "west", + "spawn_step": 2065, + "junction_id": 4, + "route_id": 186, + "lane_id": 2, + "blueprint_id": "vehicle.citroen.c3", + "target_speed": 51.54327814447726, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 1.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "4e949771", + "vehicle_id": "north_N_2_49", + "flow_name": "north", + "spawn_step": 2090, + "junction_id": 4, + "route_id": 28, + "lane_id": 2, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 57.15680656490429, + "spawn_transform": { + "location": { + "x": 8.75, + "y": 56.0, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 360.0, + "yaw": 270.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 5.25, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "north", + "exit_direction": "east", + "lane_id": 2, + "peak_density": 1.0 + } + }, + { + "event_id": "76364731", + "vehicle_id": "west_W_1_33", + "flow_name": "west", + "spawn_step": 2143, + "junction_id": 4, + "route_id": 185, + "lane_id": 1, + "blueprint_id": "vehicle.audi.a2", + "target_speed": 42.92269870609891, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": 56.0, + "y": 8.75, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "straight", + "entry_direction": "west", + "exit_direction": "east", + "lane_id": 1, + "peak_density": 1.0 + } + }, + { + "event_id": "2b04dc02", + "vehicle_id": "west_W_2_49", + "flow_name": "west", + "spawn_step": 2146, + "junction_id": 4, + "route_id": 153, + "lane_id": 2, + "blueprint_id": "vehicle.seat.leon", + "target_speed": 43.09616597375781, + "spawn_transform": { + "location": { + "x": -56.0, + "y": 1.75, + "z": 0.30000001192092896 + }, + "rotation": { + "pitch": 0.0, + "yaw": -0.0, + "roll": 0.0 + } + }, + "destination_transform": { + "location": { + "x": -1.75, + "y": 56.0, + "z": 0.0 + }, + "rotation": { + "pitch": 0.0, + "yaw": 90.0, + "roll": 0.0 + } + }, + "metadata": { + "route_type": "right", + "entry_direction": "west", + "exit_direction": "north", + "lane_id": 2, + "peak_density": 1.0 + } + } + ] +} \ No newline at end of file diff --git a/scripts/convert_xodr_to_sumo.py b/scripts/convert_xodr_to_sumo.py new file mode 100644 index 0000000..5fb1f86 --- /dev/null +++ b/scripts/convert_xodr_to_sumo.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python +""" +Convert OpenDRIVE (.xodr) files to SUMO network files. + +Usage: + python scripts/convert_xodr_to_sumo.py +""" +import os +import sys +import subprocess +from pathlib import Path + +# Check SUMO_HOME +if 'SUMO_HOME' not in os.environ: + sys.exit("ERROR: Please set SUMO_HOME environment variable") + +SUMO_TOOLS = os.path.join(os.environ['SUMO_HOME'], 'tools') +NETCONVERT = os.path.join(os.environ['SUMO_HOME'], 'bin', 'netconvert.exe') + +def convert_xodr_to_sumo(xodr_path: str, output_dir: str): + """ + Convert XODR file to SUMO network. + + Args: + xodr_path: Path to input .xodr file + output_dir: Directory to save SUMO files + """ + # Create output directory + os.makedirs(output_dir, exist_ok=True) + + # Output network file + net_file = os.path.join(output_dir, 'intersection.net.xml') + + print(f"Converting {xodr_path} to SUMO network...") + print(f"Output: {net_file}") + + # Run netconvert + cmd = [ + NETCONVERT, + '--opendrive', xodr_path, + '--output-file', net_file, + '--opendrive.curve-resolution', '1.0', # Finer resolution for accurate coordinates + '--junctions.corner-detail', '5', + '--tls.discard-loaded', 'true', # Remove ALL traffic lights (matching CARLA) + '--ramps.guess', 'true', + '--junctions.join', 'true', + '--geometry.remove', 'true', + '--no-turnarounds', 'true', # DISABLE all U-turns (prevents infinite loops) + '--no-internal-links', 'false', # Keep junction internals for realistic routing + ] + + try: + result = subprocess.run(cmd, capture_output=True, text=True) + if result.returncode == 0: + print(f"[OK] Successfully converted to {net_file}") + return net_file + else: + print(f"[ERROR] Conversion failed:") + print(result.stderr) + return None + except FileNotFoundError: + print(f"[ERROR] netconvert not found at {NETCONVERT}") + print(" Make sure SUMO is installed and SUMO_HOME is set correctly") + return None + +def create_route_file(output_dir: str, net_file: str): + """ + Create a basic route file for the intersection. + + Args: + output_dir: Directory to save route file + net_file: Path to network file + """ + route_file = os.path.join(output_dir, 'intersection.rou.xml') + + # CARLA-compatible route file template with varied routes + route_xml = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +''' + + with open(route_file, 'w') as f: + f.write(route_xml) + + print(f"[OK] Created route file: {route_file}") + return route_file + +def create_sumo_config(output_dir: str, net_file: str, route_file: str): + """ + Create SUMO configuration file. + + Args: + output_dir: Directory to save config file + net_file: Path to network file + route_file: Path to route file + """ + config_file = os.path.join(output_dir, 'intersection.sumocfg') + + # Get relative paths + net_rel = os.path.basename(net_file) + route_rel = os.path.basename(route_file) + + config_xml = f''' + + + + + + + + + + + + + + + + + + + + + +''' + + with open(config_file, 'w') as f: + f.write(config_xml) + + print(f"[OK] Created SUMO config: {config_file}") + return config_file + +def create_readme(output_dir: str): + """Create README for SUMO assets.""" + readme_file = os.path.join(output_dir, 'README.md') + + readme_content = '''# SUMO Intersection Assets + +This directory contains SUMO network files converted from OpenDRIVE format for MARL training. + +## Files + +- `intersection.net.xml` - SUMO network file (converted from XODR) +- `intersection.rou.xml` - Route definitions (managed by MARL traffic manager) +- `intersection.sumocfg` - SUMO configuration file + +## Usage + +These files are automatically loaded by `SumoMARLEnv` when using the SUMO-only training mode. + +Configuration in `configs/marl/intersection_sumo.yaml`: +```yaml +meta: + scenario_type: "intersection_sumo" + sumo_cfg: "opencda/assets/intersection_sumo/intersection.sumocfg" +``` + +## Manual Testing + +To test the SUMO network manually: +```bash +sumo-gui -c intersection.sumocfg +``` + +## Regeneration + +To regenerate these files from the XODR source: +```bash +python scripts/convert_xodr_to_sumo.py +``` +''' + + with open(readme_file, 'w') as f: + f.write(readme_content) + + print(f"[OK] Created README: {readme_file}") + +def main(): + # Paths + project_root = Path(__file__).parent.parent + xodr_file = project_root / 'opencda_marl' / 'assets' / 'maps' / 'intersection.xodr' + output_dir = project_root / 'opencda_marl' / 'assets' / 'intersection_sumo' # MARL-specific location + + print("=" * 60) + print("XODR to SUMO Conversion Script (for MARL)") + print("=" * 60) + print(f"Input XODR: {xodr_file}") + print(f"Output directory: {output_dir}") + print() + + # Check input file exists + if not xodr_file.exists(): + print(f"[ERROR] XODR file not found: {xodr_file}") + sys.exit(1) + + # Convert XODR to SUMO network + net_file = convert_xodr_to_sumo(str(xodr_file), str(output_dir)) + if not net_file: + sys.exit(1) + + # Create route file + route_file = create_route_file(str(output_dir), net_file) + + # Create SUMO config + config_file = create_sumo_config(str(output_dir), net_file, route_file) + + # Create README + create_readme(str(output_dir)) + + print() + print("=" * 60) + print("[OK] Conversion complete!") + print("=" * 60) + print() + print("Next steps:") + print("1. Test the network: sumo-gui -c", config_file) + print("2. Update configs/marl/intersection_sumo.yaml") + print("3. Run SUMO MARL training: pixi run start -t intersection_sumo --marl") + +if __name__ == '__main__': + main() diff --git a/scripts/fix_tensorboard_open3d.py b/scripts/fix_tensorboard_open3d.py new file mode 100644 index 0000000..7a6ad20 --- /dev/null +++ b/scripts/fix_tensorboard_open3d.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python +""" +Fix Open3D TensorBoard plugin compatibility issue. + +Open3D 0.19.0 has a TensorBoard plugin that tries to import O3DVisualizer, +which doesn't exist in some builds. This script: +1. Removes the plugin entry point from dist-info +2. Patches the plugin file to handle missing O3DVisualizer gracefully + +Usage: + python scripts/fix_tensorboard_open3d.py + +Or with pixi: + pixi run fix-tensorboard +""" + +import sys +import site +from pathlib import Path + + +def find_site_packages() -> list[Path]: + """Find all site-packages directories.""" + site_packages_dirs = [] + + # Get from site module + try: + site_packages_dirs.extend(site.getsitepackages()) + except: + pass + + if site.ENABLE_USER_SITE: + try: + site_packages_dirs.append(site.getusersitepackages()) + except: + pass + + # Check sys.path for site-packages directories + for path in sys.path: + if "site-packages" in path: + site_packages_dirs.append(path) + + # Also check sys.prefix + site_packages_dirs.extend([ + Path(sys.prefix) / "lib" / f"python{sys.version_info.major}.{sys.version_info.minor}" / "site-packages", + Path(sys.prefix) / "Lib" / "site-packages", # Windows + ]) + + # Remove duplicates and convert to Path objects + seen = set() + result = [] + for sp in site_packages_dirs: + sp_path = Path(sp) + sp_resolved = str(sp_path.resolve()) + if sp_resolved not in seen: + seen.add(sp_resolved) + result.append(sp_path) + + return result + + +def find_open3d_dist_info() -> Path | None: + """Find the Open3D dist-info directory in site-packages.""" + for site_packages in find_site_packages(): + if site_packages.is_dir(): + # Try both open3d and open3d_cpu patterns + for pattern in ["open3d*.dist-info"]: + for dist_info in site_packages.glob(pattern): + if dist_info.is_dir() and "open3d" in dist_info.name.lower(): + return dist_info + + return None + + +def find_open3d_package() -> Path | None: + """Find the Open3D package directory in site-packages.""" + for site_packages in find_site_packages(): + if site_packages.is_dir(): + open3d_dir = site_packages / "open3d" + if open3d_dir.is_dir(): + return open3d_dir + + return None + + +def fix_entry_points(dist_info: Path) -> bool: + """Remove TensorBoard plugin from Open3D entry points.""" + entry_points_file = dist_info / "entry_points.txt" + + if not entry_points_file.exists(): + print(f"No entry_points.txt found in {dist_info}") + return False + + content = entry_points_file.read_text() + + if "[tensorboard_plugins]" not in content: + print("TensorBoard plugin entry not found - already fixed or not present") + return True + + # Keep only console_scripts section + lines = content.strip().split("\n") + new_lines = [] + skip_section = False + + for line in lines: + if line.strip() == "[tensorboard_plugins]": + skip_section = True + continue + elif line.strip().startswith("[") and skip_section: + skip_section = False + + if not skip_section: + new_lines.append(line) + + new_content = "\n".join(new_lines).strip() + "\n" + entry_points_file.write_text(new_content) + + print(f"✓ Fixed: {entry_points_file}") + print("✓ Removed [tensorboard_plugins] section from Open3D") + return True + + +def fix_plugin_file(open3d_dir: Path) -> bool: + """Patch the TensorBoard plugin file to handle missing O3DVisualizer.""" + plugin_file = open3d_dir / "visualization" / "tensorboard_plugin" / "plugin.py" + + if not plugin_file.exists(): + print(f"Plugin file not found at {plugin_file} - skipping") + return True + + content = plugin_file.read_text() + + # Check if already patched + if "# PATCHED: Disable Open3D TensorBoard plugin" in content: + print(f"Plugin file already patched: {plugin_file}") + return True + + # Replace entire content to disable the plugin gracefully + new_content = """# PATCHED: Disable Open3D TensorBoard plugin +# The Open3D TensorBoard plugin has compatibility issues with the current +# Open3D build. This disables it while keeping TensorBoard functional. +raise ImportError("Open3D TensorBoard plugin disabled due to compatibility issues. This is normal and TensorBoard will continue to work.") +""" + + plugin_file.write_text(new_content) + print(f"✓ Patched: {plugin_file}") + return True + + +def main() -> int: + print("Fixing Open3D TensorBoard plugin compatibility issue...") + print(f"Python: {sys.executable}") + print(f"Version: {sys.version_info.major}.{sys.version_info.minor}") + print() + + # First try to find and patch the plugin file directly + open3d_dir = find_open3d_package() + if open3d_dir is None: + print("❌ Open3D package not found!") + print("Searched in:") + for sp in find_site_packages(): + print(f" {sp}") + return 1 + + print(f"✓ Found Open3D package at: {open3d_dir}") + print() + + # Fix plugin file first (most important) + if not fix_plugin_file(open3d_dir): + print("⚠ Warning: Failed to patch plugin file") + + print() + + # Fix entry points if dist-info exists + dist_info = find_open3d_dist_info() + if dist_info is None: + print("⚠ Warning: Open3D dist-info not found - skipping entry points removal") + print(" (This is okay if you're using a pixi/conda environment)") + else: + print(f"✓ Found Open3D dist-info at: {dist_info}") + if not fix_entry_points(dist_info): + print("⚠ Warning: Failed to fix entry points") + + print() + print("✓ Success! TensorBoard should now start without Open3D plugin errors.") + return 0 + + +if __name__ == "__main__": + sys.exit(main())