traffic-control / baseline_agent.py
amanmurari's picture
Upload 24 files
a871dae verified
Raw
History Blame Contribute Delete
5.2 kB
"""
Rule-based baseline agent for the Autonomous Traffic Control environment.
Uses the openenv-core EnvClient (via TrafficControlEnv) for HTTP interaction.
Demonstrates the correct sync API pattern.
Run:
# 1. Start the server (in another terminal):
uvicorn traffic_control_env.server.app:app --port 8000
# 2. Run this agent:
python -m traffic_control_env.baseline_agent
python -m traffic_control_env.baseline_agent --task emergency_priority
"""
from __future__ import annotations
import argparse
import time
from typing import Optional
from client import TrafficControlEnv
from models import TrafficAction, TrafficObservation
from models import (
PHASE_NS_GREEN, PHASE_EW_GREEN, PHASE_ALL_RED,
PHASE_NS_YELLOW, PHASE_EW_YELLOW,
)
# ---------------------------------------------------------------------------
# Rule-based policy
# ---------------------------------------------------------------------------
class RuleBasedAgent:
"""
Simple heuristic agent:
1. If any emergency vehicle is waiting, switch to their direction.
2. Otherwise, switch to the direction with more queued vehicles.
3. Enforce a minimum green time to avoid flicker.
"""
MIN_GREEN_STEPS = 4
def __init__(self) -> None:
self._steps_in_green = 0
self._last_phase = PHASE_NS_GREEN
def act(self, obs: TrafficObservation) -> TrafficAction:
current = obs.current_phase
# During yellow/transition, keep the current phase request
if current in (PHASE_NS_YELLOW, PHASE_EW_YELLOW):
return TrafficAction(light_phase=self._last_phase)
self._steps_in_green += 1
# 1. Emergency override
em_q = obs.emergency_queue
em_urg = obs.emergency_urgency
ns_em = em_q[0] + em_q[1] # North + South
ew_em = em_q[2] + em_q[3] # East + West
ns_urg = max(em_urg[0], em_urg[1]) if ns_em else 0
ew_urg = max(em_urg[2], em_urg[3]) if ew_em else 0
if ns_em > 0 or ew_em > 0:
if ns_urg >= ew_urg and current != PHASE_NS_GREEN:
self._last_phase = PHASE_NS_GREEN
self._steps_in_green = 0
return TrafficAction(light_phase=PHASE_NS_GREEN)
elif ew_urg > ns_urg and current != PHASE_EW_GREEN:
self._last_phase = PHASE_EW_GREEN
self._steps_in_green = 0
return TrafficAction(light_phase=PHASE_EW_GREEN)
# Enforce minimum green time
if self._steps_in_green < self.MIN_GREEN_STEPS:
return TrafficAction(light_phase=self._last_phase)
# 2. Queue-length balancing
q = obs.queue_lengths
ns_q = q[0] + q[1]
ew_q = q[2] + q[3]
want_phase = PHASE_NS_GREEN if ns_q >= ew_q else PHASE_EW_GREEN
if want_phase != current:
self._last_phase = want_phase
self._steps_in_green = 0
return TrafficAction(light_phase=want_phase)
# ---------------------------------------------------------------------------
# Run loop
# ---------------------------------------------------------------------------
def run(task_id: str = "basic_flow", seed: Optional[int] = 42, url: str = "http://localhost:8000"):
agent = RuleBasedAgent()
# Use the sync wrapper provided by openenv-core's EnvClient
with TrafficControlEnv(base_url=url).sync() as env:
print(f"\n=== Traffic Control Baseline Agent ===")
print(f"Task: {task_id} | Seed: {seed} | Server: {url}")
step_result = env.reset(task_id=task_id, seed=seed)
step = 0
while not step_result.done:
obs = step_result.observation
action = agent.act(obs)
step_result = env.step(action)
obs = step_result.observation
step += 1
if step % 20 == 0:
q = obs.queue_lengths
em_q = obs.emergency_queue
print(
f" Step {step:3d} | Phase {obs.current_phase} | "
f"Q [N{q[0]},S{q[1]},E{q[2]},W{q[3]}] | "
f"EmQ [N{em_q[0]},S{em_q[1]},E{em_q[2]},W{em_q[3]}] | "
f"Reward {step_result.reward:+.2f}"
)
state = env.state()
print(f"\n=== Episode Ended (step {step}) ===")
print(f" Total vehicles: {state.total_vehicles_passed}")
print(f" Emergency passed: {state.total_emergency_passed}")
print(f" Total waiting: {state.total_waiting_time:.1f}")
print(f" Collisions: {state.total_collisions}")
print(f" Phase changes: {state.total_phase_changes}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Rule-based baseline agent")
parser.add_argument("--task", default="basic_flow",
choices=["basic_flow", "emergency_priority", "dynamic_scenarios"])
parser.add_argument("--seed", type=int, default=42)
parser.add_argument("--url", default="http://localhost:8000")
args = parser.parse_args()
run(task_id=args.task, seed=args.seed, url=args.url)