1

我是编程新手,使用 Google OR-tools 创建我的 VRP 模型。在我当前的模型中,我已经包含了一个一般的时间窗口和每辆车的容量限制,从而创建了一个带有时间窗口的容量化车辆路线问题。我遵循了 OR-tools 指南,其中包含每辆车的最长旅行时间。

但是,我想包括所有路线总和的最大旅行持续时间,而每辆车的最大旅行持续时间并不重要(因此我将其设置为 100.000)。因此,我想在模型/解决方案打印机中创建一些东西,告诉我由于对所有路线总和的最大旅行持续时间的限制,无法访问哪些地址。从我看到的示例中,我认为这很容易,但是我对编程的了解相当有限,因此我的尝试没有成功。谁能帮我?

import pandas as pd
import openpyxl
import numpy as np
import math
from random import sample
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
from scipy.spatial.distance import squareform, pdist
from haversine import haversine


#STEP - create data
# import/read excel file
data = pd.read_excel(r'C:\Users\Jean-Paul\Documents\Thesis\OR TOOLS\Data.xlsx', engine = 'openpyxl')
df = pd.DataFrame(data, columns= ['number','lat','lng'])                        # create dataframe with 10805 addresses + address of the depot
#print (df)

# randomly sample X addresses from the dataframe and their corresponding number/latitude/longtitude
df_sample = df.sample(n=100)
#print (df_data)

# read first row of the excel file (= coordinates of the depot)
df_depot = pd.DataFrame(data, columns= ['number','lat','lng']).iloc[0:1]
#print (df_depot)

# combine dataframe of depot and sample into one dataframe
df_data = pd.concat([df_depot, df_sample], ignore_index=True, sort=False)
#print (df_data)


#STEP - create distance matrix data
# determine distance between latitude and longtitude
df_data.set_index('number', inplace=True)
matrix_distance = pd.DataFrame(squareform(pdist(df_data, metric=haversine)), index=df_data.index, columns=df_data.index)
matrix_list = np.array(matrix_distance)
#print (matrix_distance)                                                        # create table of distances between addresses including headers
#print (matrix_list)                                                            # converting table to list of lists and exclude headers

#STEP - create time matrix data
travel_time = matrix_list / 15 * 60                                             # divide distance by travel speed 20 km/h and multiply by 60 minutes
#print (travel_time)                                                            # converting distance matrix to travel time matrix


#STEP - create time window data
# create list for each sample - couriers have to visit this address within 0-X minutes of time using a list of lists
window_range = []
for i in range(len(df_data)):
    list = [0, 240]
    window_range.append(list)                                                   # create list of list with a time window range for each address
#print (window_range)


#STEP - create demand data
# create list for each sample - all addresses demand 1 parcel except the depot
demand_range = []
for i in range(len(df_data.iloc[0:1])):
    list = 0
    demand_range.append(list)
for j in range(len(df_data.iloc[1:])):
    list2 = 1
    demand_range.append(list2)
#print (demand_range)


#STEP - create fleet size data              # amount of vehicles in the fleet
fleet_size = 6
#print (fleet_size)


#STEP - create capacity data for each vehicle
fleet_capacity = []
for i in range(fleet_size):                 # capacity per vehicle
    list = 20
    fleet_capacity.append(list)
#print (fleet_capacity)


#STEP - create data model that stores all data for the problem
def create_data_model():
    data = {}
    data['time_matrix'] = travel_time
    data['time_windows'] = window_range
    data['num_vehicles'] = fleet_size
    data['depot'] = 0                     # index of the depot
    data['demands'] = demand_range
    data['vehicle_capacities'] = fleet_capacity
    return data


#STEP - creating the solution printer
def print_solution(data, manager, routing, solution):
    """Prints solution on console."""
    print(f'Objective: {solution.ObjectiveValue()}')
    time_dimension = routing.GetDimensionOrDie('Time')
    total_time = 0
    for vehicle_id in range(data['num_vehicles']):
        index = routing.Start(vehicle_id)
        plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
        while not routing.IsEnd(index):
            time_var = time_dimension.CumulVar(index)
            plan_output += '{0} Time({1},{2}) -> '.format(
                manager.IndexToNode(index), solution.Min(time_var),
                solution.Max(time_var))
            index = solution.Value(routing.NextVar(index))
        time_var = time_dimension.CumulVar(index)
        plan_output += '{0} Time({1},{2})\n'.format(manager.IndexToNode(index),
                                                    solution.Min(time_var),
                                                    solution.Max(time_var))
        plan_output += 'Time of the route: {}min\n'.format(
            solution.Min(time_var))
        print(plan_output)
        total_time += solution.Min(time_var)
    print('Total time of all routes: {}min'.format(total_time))


#STEP - create the VRP solver
def main():
    # instantiate the data problem
    data = create_data_model()

    # create the routing index manager
    manager = pywrapcp.RoutingIndexManager(len(data['time_matrix']),
                                           data['num_vehicles'], data['depot'])
    # create routing model
    routing = pywrapcp.RoutingModel(manager)


#STEP - create demand callback and dimension for capacity
    # create and register a transit callback
    def demand_callback(from_index):
        """Returns the demand of the node."""
        # convert from routing variable Index to demands NodeIndex
        from_node = manager.IndexToNode(from_index)
        return data['demands'][from_node]

    demand_callback_index = routing.RegisterUnaryTransitCallback(
        demand_callback)
    routing.AddDimensionWithVehicleCapacity(
        demand_callback_index,
        0,                              # null capacity slack
        data['vehicle_capacities'],     # vehicle maximum capacities
        True,                           # start cumul to zero
        'Capacity')


#STEP - create time callback
    # create and register a transit callback
    def time_callback(from_index, to_index):
        """Returns the travel time between the two nodes."""
        # convert from routing variable Index to time matrix NodeIndex
        from_node = manager.IndexToNode(from_index)
        to_node = manager.IndexToNode(to_index)
        return data['time_matrix'][from_node][to_node]

    transit_callback_index = routing.RegisterTransitCallback(time_callback)

    # define cost of each Arc (costs in terms of travel time)
    routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)


# STEP - create a dimension for the travel time (TIMEWINDOW) - dimension keeps track of quantities that accumulate over a vehicles route
# add time windows constraint
    time = 'Time'
    routing.AddDimension(
        transit_callback_index,
        2,                           # allow waiting time (does not have an influence in this model)
        100000,                      # maximum total route lenght in minutes per vehicle (does not have an influence because of capacity constraint)
        False,                       # do not force start cumul to zero
        time)
    time_dimension = routing.GetDimensionOrDie(time)

# add time window constraints for each location except depot
    for location_idx, time_window in enumerate(data['time_windows']):
        if location_idx == data['depot']:
            continue
        index = manager.NodeToIndex(location_idx)
        time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])

# add time window constraint for each vehicle start node
    depot_idx = data['depot']
    for vehicle_id in range(data['num_vehicles']):
        index = routing.Start(vehicle_id)
        time_dimension.CumulVar(index).SetRange(
            data['time_windows'][depot_idx][0],
            data['time_windows'][depot_idx][1])


#STEP - instantiate route start and end times to produce feasible times
    for i in range(data['num_vehicles']):
        routing.AddVariableMinimizedByFinalizer(
            time_dimension.CumulVar(routing.Start(i)))
        routing.AddVariableMinimizedByFinalizer(
            time_dimension.CumulVar(routing.End(i)))


#STEP - setting default search parameters and a heuristic method for finding the first solution
    search_parameters = pywrapcp.DefaultRoutingSearchParameters()
    search_parameters.first_solution_strategy = (
        routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)


#STEP - solve the problem with the serach parameters and print solution
    solution = routing.SolveWithParameters(search_parameters)
    if solution:
        print_solution(data, manager, routing, solution)
if __name__ == '__main__':
    main()
4

0 回答 0