Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[sumo-user] Issue regarding re-routing after onboarding person with TaxiService

I have completed this much code and attached the file her, in this code taxi only onboard the passenger from the current edge if possible or person is available on the next edge, I want that taxi should onboard passenger from upcoming edge until it's capacity is full. After onboarding, taxi should go with the probabilistic route as given in the function decide_next_idle_junction_for_taxi(). Taxi is allowed to take maximum 2 diversion from the shortest paths which will be given by the function of get_all_shortest_routes(). If the upcoming edge which taxi is taking is not the matching the upcoming edge from any of the possible shortest route then it will considered as diversion. For example: taxi is having shortest possible route as 1, 2, 3, 4, taxi is currently on edge 1, and as per function it is going on the route 1, 5, then from 5th edge it goes to 3 as per function and then 4 so final route will be 1, 5, 3, 4 so here taxi has taken one diversion. If taxi has completed 2 diversion then let taxi decide it's own route as per dispatchTaxi() . 

I am not able to do the rerouting after onboarding the passenger, it is removing the taxis from the simulation, what's the issue with this? How can I solve this? 

Thanks, 
Jay
from collections import defaultdict
import traci
import sumolib
import numpy as np
import random
import networkx as nx

net = sumolib.net.readNet("grid.net.xml")
  # Get special edges (e.g., bus lanes, metro tracks)
edge_ids = [e.getID() for e in net.getEdges() if not e.isSpecial()]
ped_id_counter = 0
od_matrix = {}
DAMPING_FACTOR_ZETA = 0.5
SCALE_DISTANCE = 100.0
# GRID_SIZE = 250  # Size of each grid cell
# spatial_grid = {}  # Dictionary to store passengers in grid cells

train_stops = {
    "E2": {
        "down": {"id": "E0_stop_down", "lane": "E1E2", "pos": 460},
        "up": {"id": "E0_stop_up", "lane": "E3E2", "pos": 460}
    },
    "E4": {
        "down": {"id": "E4_stop_down", "lane": "E3E4", "pos": 460},
        "up": {"id": "E4_stop_up", "lane": "E5E4", "pos": 460}
    },
    "E7": {
        "down": {"id": "E7_stop_down", "lane": "E6E7", "pos": 460},
        "up": {"id": "E7_stop_up", "lane": "E8E7", "pos": 460}
    },
    "C4": {
        "down": {"id": "C4_stop_down", "lane": "B4C4", "pos": 460},
        "up": {"id": "C4_stop_up", "lane": "D4C4", "pos": 460}
    },
    "H4": {
        "down": {"id": "E7_stop_down", "lane": "G4H4", "pos": 460},
        "up": {"id": "E7_stop_up", "lane": "I4H4", "pos": 460}
    }
}

def get_lambda(current_minute):
    """Return pedestrian spawn rate based on scaled day cycle (120 minutes total)"""

    if 0 <= current_minute < 56:
        return 6      # Night
    elif 56 <= current_minute < 86:
        return 15     # Off-peak
    elif 86 <= current_minute < 116:
        return 30     # Peak
    elif 116 <= current_minute < 160:
        return 13     # Off-peak
    elif 160 <= current_minute < 206:
        return 30     # Peak
    elif 206 <= current_minute <= 240:
        return 13     # Off-peak
    else:
        return 15     # Fallback default


def get_random_route(from_edge, to_stop):
    """Get a valid walking route to a train stop"""
    try:
        from_edge_obj = net.getEdge(from_edge)
        to_edge_obj = net.getEdge(to_stop["lane"].split('_')[0])

        route = net.getShortestPath(from_edge_obj, to_edge_obj)[0]
        if route:
            return [edge.getID() for edge in route]
        return None
    except:
        return None

def get_junction_coordinates(net_file):
    """
    Extract junction coordinates from SUMO .net.xml file.
    Returns a dictionary of {junction_id: (x, y)}.
    """
    net = sumolib.net.readNet(net_file)
    junctions = {
        j.getID(): (j.getCoord()[0], j.getCoord()[1])
        for j in net.getNodes()
        if not j.getID().startswith(":")  # Ignore internal junctions
    }
    return junctions


def find_center_junction(junctions):
    """
    Find junction closest to the geometric center (centroid).
    """
    coords = np.array(list(junctions.values()))
    centroid = np.mean(coords, axis=0)

    min_dist = float("inf")
    center_junction = None
    for junction_id, (x, y) in junctions.items():
        dist = np.hypot(x - centroid[0], y - centroid[1])
        if dist < min_dist:
            min_dist = dist
            center_junction = junction_id
    return center_junction


def compute_attractiveness(junctions, center_junction, peak_lambda=30, decay_beta=0.0005):
    """
    Compute attractiveness values for all junctions using exponential decay.
    """
    center_x, center_y = junctions[center_junction]
    attractiveness = {}
    for junction_id, (x, y) in junctions.items():
        dist = np.hypot(x - center_x, y - center_y)
        lam = peak_lambda * np.exp(-decay_beta * dist)
        attractiveness[junction_id] = lam
    return attractiveness


def initialize_attractiveness():
    """
    Initialize attractiveness map by reading junctions from SUMO network file.
    Assumes 'grid.net.xml' is in current directory.
    Returns: {junction_id: attractiveness λ}
    """
    net_file = "grid.net.xml"  # Path to the network file
    junctions = get_junction_coordinates(net_file)
    center_junction = find_center_junction(junctions)
    attractiveness_map = compute_attractiveness(junctions, center_junction)
    return attractiveness_map


# Example usage:
attractiveness_map = initialize_attractiveness()

# Preview top 5 most attractive junctions
for junction, lam in sorted(attractiveness_map.items(), key=lambda x: -x[1])[:5]:
    print(f"Junction: {junction}, Attractiveness: {lam:.2f}")

def get_random_destination():
    station = random.choice(list(train_stops.keys()))

    direction = random.choice(["up", "down"])

    return train_stops[station][direction]

def is_valid_route(route):
    """Check if route pattern is valid for taxis"""
    route_ids = [edge.getID() for edge in route]

    # Check consecutive edges
    for i in range(len(route_ids) - 1):
        current = route_ids[i]
        next_edge = route_ids[i + 1]

        # Skip if edges don't match AxAy pattern
        if (len(current) != 4 or len(next_edge) != 4 or
            not (current.startswith('A') and next_edge.startswith('A'))):
            continue

        # Check if edges are reciprocal (A1A2 followed by A2A1)
        if (current[1] == next_edge[3] and
            current[3] == next_edge[1] and
            current[2] == next_edge[2]):
            return False

    return True


# def change_person_destination(person_id, new_lane):
#     try:
#         # Get current stage
#         stage = traci.person.getStage(person_id)

#         if stage.type != traci.constants.STAGE_DRIVING:
#             print(f"[Skip] Person {person_id} is not in a driving stage.")
#             return

#         vehicle_id = traci.person.getVehicle(person_id)
#         # if not vehicle_id:
#         #     return
#         # route = stage.edges  # list of edges

#         # Find current pickup edge
#         current_edge = traci.person.getRoadID(person_id)
#         if not current_edge:
#             print(f"[Error] Could not get current road for {person_id}")
#             return

#         # Rebuild a new driving stage to the new lane
#         # You must end the current stage and append a new one

#         # Clear current stages: remove person from vehicle
#         traci.person.removeStages(person_id)
#         traci.person.appendDrivingStage(person_id, new_lane, "taxi")

#         print(f"[Updated] Person {person_id}'s destination changed to lane {new_lane}")

#     except Exception as e:
#         print(f"[Error] Failed to change destination for {person_id}: {e}")


def spawn_pedestrians(step):
    """
    Spawn pedestrians with higher probability near attractive junctions.
    """
    global ped_id_counter, attractiveness_map
    
    current_min = step // 60
    base_lambda = get_lambda(current_min)
    
    # print(f"\n[Pedestrian Spawn] Step={step}, Minute={current_min}, Base λ={base_lambda}")

    for edge in edge_ids:
        # Get the "from" junction of this edge
        from_junction = edge[:2]  # e.g., 'C3C4' -> 'C3'
        
        # Get attractiveness-modified spawn rate
        local_lambda = attractiveness_map.get(from_junction, base_lambda) / 100.0
        
        num_peds = np.random.poisson(local_lambda)
        
        for _ in range(num_peds):
            destination = get_random_destination()
            route = get_random_route(edge, destination)
            
            if not route:
                continue
                
            ped_id = f"ped_{ped_id_counter}"
            ped_id_counter += 1
            
            try:
                # Random position along edge
                pos = random.randint(30, 450)
                
                # Add person to simulation
                traci.person.add(ped_id, edge, depart=step, pos=pos)
                
                # Calculate best arrival position
                distances = {
                    20: abs(pos - 20),
                    255: abs(pos - 255),
                    480: abs(pos - 480)
                }
                arrival_pos = min(distances, key=distances.get)
                
                # Add walking stage
                traci.person.appendWalkingStage(
                    ped_id,
                    route[0],
                    arrivalPos=arrival_pos
                )
                
                # Add taxi stage
                traci.person.appendDrivingStage(
                    ped_id,
                    destination["lane"],
                    lines="taxi"
                )
                
                # print(f"  Spawned pedestrian {ped_id} at {edge} (pos={pos})")
                
            except Exception as e:
                print(f"Error spawning pedestrian: {str(e)}")

def get_all_shortest_routes(net_file, source_edge_id, destination_edge_id):
    net = sumolib.net.readNet(net_file)
    G = nx.DiGraph()
    for edge in net.getEdges():
        if edge.isSpecial():
            continue
        for succ in edge.getOutgoing():
            if not succ.isSpecial():
                G.add_edge(edge.getID(), succ.getID(), weight=edge.getLength())
    if source_edge_id not in G or destination_edge_id not in G:
        return []
    try:
        shortest_length = nx.shortest_path_length(G, source=source_edge_id, target=destination_edge_id)
    except nx.NetworkXNoPath:
        return []
    all_shortest_paths = list(nx.all_simple_paths(G, source=source_edge_id, target=destination_edge_id, cutoff=shortest_length))
    shortest_routes = [path for path in all_shortest_paths if len(path) - 1 == shortest_length]
    return shortest_routes


# Add this at the top of your file with other global variables
# Add at the top with other global variables
taxi_assignments = {}

dispatched_taxis = {}
assigned_reservations = set()

# Add these new global variables at the top with other globals
taxi_current_passengers = {}  # Store current passengers in each taxi
taxi_capacity = 4  # Maximum passengers per taxi
taxi_routes = {}  # Store current routes of taxis

taxi_id_counter = 0

def decide_next_junction_for_idle_taxi(taxi_id, net, attractiveness_map, damping_factor=0.8):
    try:
        current_edge_id = traci.vehicle.getRoadID(taxi_id)

        # Skip internal lanes (junctions like ":C3_0")
        if current_edge_id.startswith(":"):
            # print(f"[Skip] Taxi {taxi_id} is currently in internal junction: {current_edge_id}")
            return

        current_edge = net.getEdge(current_edge_id)
        from_node = current_edge.getToNode()  # The junction this edge leads to

        outgoing_edges = from_node.getOutgoing()
        if not outgoing_edges:
            # print(f"[Dead End] No outgoing edges from junction {from_node.getID()} for taxi {taxi_id}")
            return

        # print(f"\n[Routing] Taxi {taxi_id} at edge {current_edge_id} reaching junction {from_node.getID()}")
        # print(f"→ Evaluating outgoing edges from junction {from_node.getID()}:")

        # Compute attractiveness scores
        edge_utilities = {}
        total = 0
        for edge in outgoing_edges:
            to_junction = edge.getToNode().getID()
            attractiveness = attractiveness_map.get(to_junction, 0)
            utility = damping_factor * attractiveness
            edge_utilities[edge.getID()] = utility
            total += utility

            # print(f"   - Edge {edge.getID()} leads to junction {to_junction} with attractiveness {attractiveness:.3f} → utility {utility:.3f}")

        cumulative = []
        acc = 0
        for edge_id, utility in edge_utilities.items():
            prob = utility / total
            acc += prob
            cumulative.append((edge_id, acc))

        r = np.random.uniform(0, 1)
        # print(f"[Prob] Random value: {r:.4f}")

        selected_edge_id = None
        for edge_id, upper_bound in cumulative:
            if r <= upper_bound:
                selected_edge_id = edge_id
                break

        if selected_edge_id is None:
            # print("[Error] No edge selected — check distribution logic.")
            return

        path_edges = net.getShortestPath(net.getEdge(current_edge_id), net.getEdge(selected_edge_id))[0]
        if path_edges is None:
            # print(f"[Warning] No path found from {current_edge_id} to {selected_edge_id}")
            return None

        route = [edge.getID() for edge in path_edges]

        traci.vehicle.setRoute(taxi_id, route)

        # print(f"[Selected] Probabilistically chose edge {selected_edge_id} → junction {net.getEdge(selected_edge_id).getToNode().getID()}")

        return selected_edge_id

    except Exception as e:
        print(f"[Error] While routing taxi {taxi_id}: {e}")

# dispatch_logs = {}
# LOG_FILENAME = "taxi_dispatch_log.txt"

taxi_destinations = {}   # taxi_id → fixed destination edge
taxi_diversions = {}     # taxi_id → current diversion count
taxi_paths = {}          # taxi_id → list of shortest paths (list of lists)

def controlled_routing_after_onboarding(taxi_id):
    """
    After onboarding, control the taxi's route:
    - Follow probabilistic routing, but allow max 2 diversions from any shortest path.
    - If 2 diversions are taken, let taxi route itself (do not override).
    """
    global taxi_destinations, taxi_diversions, taxi_paths

    # If taxi is not dispatched or has no destination, skip
    if taxi_id not in taxi_destinations or taxi_id not in taxi_paths:
        return

    # If taxi has already reached destination, skip
    current_edge = traci.vehicle.getRoadID(taxi_id)
    dest_edge = taxi_destinations[taxi_id]
    if current_edge == dest_edge:
        return

    # If max diversions reached, let taxi route itself
    if taxi_diversions.get(taxi_id, 0) >= 2:
        return

    # Get all shortest routes for this taxi
    shortest_routes = taxi_paths[taxi_id]
    if not shortest_routes:
        return

    # Find all possible next edges from current position in any shortest route
    possible_next_edges = set()
    for route in shortest_routes:
        if current_edge in route:
            idx = route.index(current_edge)
            if idx < len(route) - 1:
                possible_next_edges.add(route[idx + 1])

    # Probabilistically select next edge
    next_edge = decide_next_junction_for_idle_taxi(taxi_id, net, attractiveness_map)

    if not next_edge:
        return

    # Check if next_edge is a valid next step in any shortest route
    if next_edge in possible_next_edges:
        # Not a diversion, continue
        traci.vehicle.setRoute(taxi_id, [current_edge, next_edge])
    else:
        # Diversion taken
        taxi_diversions[taxi_id] = taxi_diversions.get(taxi_id, 0) + 1
        traci.vehicle.setRoute(taxi_id, [current_edge, next_edge])
        print(f"[Taxi {taxi_id}] Diversion taken! Total diversions: {taxi_diversions[taxi_id]}")
        if taxi_diversions[taxi_id] >= 2:
            print(f"[Taxi {taxi_id}] Max diversions reached. Letting taxi route itself.")
    
def manage_taxi_fleet(step):
    global dispatched_taxis, dispatch_logs, net, attractiveness_map, assigned_reservations, taxi_assignments, taxi_current_passengers, taxi_routes
    global taxi_destinations, taxi_diversions, taxi_paths, taxi_capacity

    # Get current reservations and available taxis
    reservations = [r for r in traci.person.getTaxiReservations(0) if r.id not in assigned_reservations]
    fleet = list(traci.vehicle.getTaxiFleet(0))
    fleet = [taxi for taxi in fleet if taxi not in dispatched_taxis]
    available_taxis = [taxi for taxi in fleet if taxi not in dispatched_taxis]

    if not reservations or not fleet:
        return

    # Group reservations by (from_edge, destination_lane)
    grouped = {}
    res_map = {}
    for res in reservations:
        # print(res)
        person_id = res.persons[0] 
        person_info = traci.person.getStage(person_id)
        # print(person_info)
        to_junction = person_info.edges[1][2:4]
        group_key = (person_info.edges[0], to_junction)
        res_map[res.id] = res
        if group_key not in grouped:
            grouped[group_key] = []
        # Add reservation id to the last sublist if not full, else start a new sublist
        if not grouped[group_key] or len(grouped[group_key][-1]) >= taxi_capacity:
            grouped[group_key].append([])
        grouped[group_key][-1].append(res.id)

    assigned_taxis = 0

    from math import dist

    for taxi_id in fleet[:]:
        try:
            if traci.vehicle.getPersonNumber(taxi_id) > 0:
                continue  # Already onboarded someone

            current_edge = traci.vehicle.getRoadID(taxi_id)
            taxi_pos = traci.vehicle.getPosition(taxi_id)
            next_edge = decide_next_junction_for_idle_taxi(taxi_id, net, attractiveness_map)

            # STEP 1: Try to onboard on CURRENT EDGE using distance > 5m
            onboarded_reservations = []
            dest_junction = None

            for res in reservations:
                if res.id in assigned_reservations:
                    continue

                person_id = res.persons[0]
                person_info = traci.person.getStage(person_id)

                if person_info.edges[0] != current_edge:
                    continue

                person_pos = traci.person.getPosition(person_id)
                if dist(taxi_pos, person_pos) <= 5:
                    continue  # Too close; skip this passenger

                # First person defines the destination
                if not dest_junction:
                    dest_junction = person_info.edges[1][2:4]
                    group_key = (current_edge, dest_junction)

                # Allow only same-destination passengers
                if person_info.edges[1][2:4] == dest_junction:
                    if len(onboarded_reservations) >= taxi_capacity:
                        break
                    onboarded_reservations.append(res.id)

            if onboarded_reservations:
                dispatch_list = onboarded_reservations + onboarded_reservations
                traci.vehicle.dispatchTaxi(taxi_id, dispatch_list)
                dispatched_taxis[taxi_id] = onboarded_reservations.copy()
                taxi_current_passengers[taxi_id] = onboarded_reservations.copy()
                for rid in onboarded_reservations:
                    assigned_reservations.add(rid)
                available_taxis.remove(taxi_id)
                destination_edge = person_info.edges[1]
                shortest_routes = get_all_shortest_routes("grid.net.xml", current_edge, destination_edge)
                taxi_destinations[taxi_id] = destination_edge
                taxi_paths[taxi_id] = shortest_routes
                taxi_diversions[taxi_id] = 0
                assigned_taxis += 1
                print(f"[Step {step}] Taxi {taxi_id} onboarded from current edge: {onboarded_reservations}")


                continue  # Taxi is now busy, skip to next

            # STEP 2: Try to onboard on NEXT EDGE
            if not next_edge:
                continue

            # Find nearest reservation on that edge
            edge_reservations = []
            for res in reservations:
                if res.id in assigned_reservations:
                    continue
                person_id = res.persons[0]
                person_info = traci.person.getStage(person_id)
                if person_info.edges[0] != next_edge:
                    continue
                edge_reservations.append((res, res.departPos))

            if not edge_reservations:
                continue

            # Sort by departPos (nearest first)
            edge_reservations.sort(key=lambda x: x[1])
            base_res = edge_reservations[0][0]
            base_person_info = traci.person.getStage(base_res.persons[0])
            dest_junction = base_person_info.edges[1][2:4]
            group_key = (next_edge, dest_junction)

            # Gather rest of group with same destination
            onboarded_group = []
            for res, _ in edge_reservations:
                if res.id in assigned_reservations:
                    continue
                person_info = traci.person.getStage(res.persons[0])
                if person_info.edges[1][2:4] == dest_junction:
                    if len(onboarded_reservations) >= taxi_capacity:
                        break
                    onboarded_group.append(res.id)
                if len(onboarded_group) >= taxi_capacity:
                    break

            if onboarded_group:
                dispatch_list = onboarded_group + onboarded_group
                traci.vehicle.dispatchTaxi(taxi_id, dispatch_list)
                dispatched_taxis[taxi_id] = onboarded_group.copy()
                taxi_current_passengers[taxi_id] = onboarded_group.copy()
                for rid in onboarded_group:
                    assigned_reservations.add(rid)
                available_taxis.remove(taxi_id)

                destination_edge = person_info.edges[1]
                shortest_routes = get_all_shortest_routes("grid.net.xml", current_edge, destination_edge)
                taxi_destinations[taxi_id] = destination_edge
                taxi_paths[taxi_id] = shortest_routes
                taxi_diversions[taxi_id] = 0
                assigned_taxis += 1
                print(f"[Step {step}] Taxi {taxi_id} onboarded from next edge: {onboarded_group}")

        except traci.TraCIException as e:
            print(f"[Error] Failed to manage taxi {taxi_id}: {e}")
            continue

    if assigned_taxis == 0:
        print(f"[Step {step}] No taxis dispatched this round.")

    for taxi_id in list(dispatched_taxis.keys()):
        try:
            controlled_routing_after_onboarding(taxi_id)

        except traci.TraCIException as e:
            print(f"[Error] Failed to process taxi {taxi_id}: {e}")
            continue

    # Reset taxis that finished all trips
    # reset_taxi_after_dropoff()



    # for taxi_id in fleet:
    #     if not grouped:
    #         break

    #     selected_group = None
    #     for key in grouped:
    #         if key not in assigned_keys and len(grouped[key]) > 0:
    #             selected_group = key
    #             break

    #     if not selected_group:
    #         break

    #     group_lists = grouped[selected_group]
    #     assigned_keys.add(selected_group)

    #     for sublist in group_lists:
    #         if not sublist:
    #             continue
    #         # Mark reservations as assigned
    #         for pid in sublist:
    #             assigned_reservations.add(pid)
    #             used_reservations.add(pid)
    #         # Dispatch taxi for this sublist (repeat list for pickup/drop)
    #         dispatch_list = sublist + sublist
    #         try:
    #             traci.vehicle.dispatchTaxi(taxi_id, dispatch_list)
    #             dispatched_taxis[taxi_id] = sublist.copy()
    #             taxi_current_passengers[taxi_id] = sublist.copy()
    #             # Move to next taxi for next sublist
    #             break
    #         except traci.TraCIException as e:
    #             print(f"Dispatch error for taxi {taxi_id}: {e}")
    #             continue


def spawn_taxis(step):
    """
    Spawn taxis with higher probability near attractive junctions.
    Taxis are more likely to spawn in areas with high pedestrian demand.
    """
    global taxi_id_counter, attractiveness_map

    current_min = step // 60
    base_lambda = get_lambda(current_min) / 4  # Divide by 4 to maintain reasonable taxi numbers

    # total_attr = sum(attractiveness_map.get(edge[:2], 0) for edge in edge_ids)
    # if total_attr == 0:
    #     total_attr = 1

    # print(f"\n[Taxi Spawn] Step={step}, Minute={current_min}, Base λ={base_lambda}")

    for edge in edge_ids:
        # Get the origin junction of this edge
        from_junction = edge[:2]  # e.g., 'C3C4' -> 'C3'
        
        local_lambda = attractiveness_map.get(from_junction, base_lambda) / 400.0
        num_taxis = np.random.poisson(local_lambda)

        for _ in range(num_taxis):
            taxi_id = f"taxi_{taxi_id_counter}"
            
            try:
                # Find a valid route from current edge
                # Using a major junction (like E4) as common destination for initial routing
                destination_edge = "E4E5"  # Can be adjusted based on your network
                route = net.getShortestPath(net.getEdge(edge), net.getEdge(destination_edge))[0]

                if route and is_valid_route(route):
                    route_edges = [e.getID() for e in route]
                    
                    # Limit initial route length
                    initial_route = route_edges[:5]
                    
                    # Create and add route
                    traci.route.add(f"route_{taxi_id}",initial_route)
                    # Add taxi to simulation
                    traci.vehicle.add(
                        vehID=taxi_id,
                        routeID=f"route_{taxi_id}",
                        typeID="taxi",
                        depart=step
                    )
                    # traci.vehicle.setSpeed(taxi_id,)

                    # Initialize taxi state tracking
                    taxi_routes[taxi_id] = initial_route
                    taxi_current_passengers[taxi_id] = []

                    # print(f"→ Taxi {taxi_id} spawned at {edge} (λ={local_lambda:.2f})")
                    # print(f"  Initial route: {' → '.join(initial_route)}")
                    
                    taxi_id_counter += 1

            except traci.TraCIException as e:
                print(f"Error spawning taxi {taxi_id}: {e}")
                continue

def remove_empty_taxis(step):
    """Remove empty taxis when lambda is 13"""
    current_min = step // 60
    lam = get_lambda(current_min)

    if lam == 13:
        fleet = list(traci.vehicle.getTaxiFleet(0))
        empty_taxis = [tid for tid in fleet if tid not in taxi_current_passengers]

        empty_taxis.sort(key=lambda x: int(x.split('_')[1]), reverse=True)

        to_remove = min(lam, len(empty_taxis))

        print(f"\n=== Removing Empty Taxis at step {step} ===")
        print(f"Found {len(empty_taxis)} empty taxis")
        print(f"Attempting to remove {to_remove} taxis")

        removed = 0
        for i in range(to_remove):
            taxi_id = empty_taxis[i]
            try:
                traci.vehicle.remove(taxi_id, reason=2)
                if taxi_id in taxi_routes:
                    del taxi_routes[taxi_id]
                removed += 1
                print(f"Removed empty taxi {taxi_id}")
            except traci.TraCIException as e:
                print(f"Error removing taxi {taxi_id}: {e}")

        print(f"Successfully removed {removed} empty taxis")

Back to the top