import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
# ------------------ 数据读取与预处理 ------------------
customers_df = pd.read_excel( r"D:\Matlab\code111\数模作业\题目4\研模训练题4\附件1.xlsx", sheet_name='客户信息', header=None, skiprows=8, nrows=31)
distance_matrix = pd.read_excel( r"D:\Matlab\code111\数模作业\题目4\研模训练题4\附件1.xlsx", sheet_name='距离矩阵', header=None, skiprows=1, nrows=31, usecols="B:AF").values.astype(int)
# 提取静态客户属性(不含depot)
num_static_customers = len(customers_df)
demands = customers_df.iloc[:, 3].astype(int).tolist()
ready_time = customers_df.iloc[:, 4].astype(int).tolist()
due_time = customers_df.iloc[:, 5].astype(int).tolist()
service_time = customers_df.iloc[:, 6].astype(int).tolist()
# 初始 depot 设置为 0
depot = 0
num_vehicles = 25
vehicle_capacities = [200] * num_vehicles
# ------------------ 第一阶段:仅静态客户路径规划 ------------------
def solve_initial_problem():
data = {
'distance_matrix': distance_matrix,
'demands': demands,
'ready_time': ready_time,
'due_time': due_time,
'service_time': service_time,
'num_vehicles': num_vehicles,
'vehicle_capacities': vehicle_capacities,
'depot': depot
}
manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']), data['num_vehicles'], data['depot'])
routing = pywrapcp.RoutingModel(manager)
# 距离回调函数
def distance_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return data['distance_matrix'][from_node][to_node]
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
# 容量维度
def demand_callback(from_index):
from_node = manager.IndexToNode(from_index)
return data['demands'][from_node]
demand_callback_index = routing.RegisterUnaryTransitCallback(demand_callback)
routing.AddDimensionWithVehicleCapacity(
demand_callback_index, 0, data['vehicle_capacities'], True, 'Capacity'
)
# 时间维度
def time_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
travel_time = data['distance_matrix'][from_node][to_node]
return travel_time + data['service_time'][from_node]
time_callback_index = routing.RegisterTransitCallback(time_callback)
routing.AddDimension(
time_callback_index,
2000, # 最大等待时间
2000, # 最大行程时间
False, # 不允许等待
'Time'
)
time_dimension = routing.GetDimensionOrDie('Time')
for location_idx in range(len(data['distance_matrix'])):
index = manager.NodeToIndex(location_idx)
if location_idx != data['depot']:
time_dimension.CumulVar(index).SetRange(
data['ready_time'][location_idx],
data['due_time'][location_idx]
)
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH # ✅ 正确拼写
)
search_parameters.time_limit.FromSeconds(10)
solution = routing.SolveWithParameters(search_parameters)
print("✅ 初始路径规划完成")
return data, manager, routing, solution
# ------------------ 动态客户生成(模拟第30分钟后新增)------------------
def generate_dynamic_customers():
np.random.seed(42)
dynamic_demands = np.random.randint(1, 11, size=5).tolist()
dynamic_ready_time = np.random.randint(30, 60, size=5).tolist()
dynamic_due_time = [rt + np.random.randint(60, 120) for rt in dynamic_ready_time]
dynamic_service_time = np.random.randint(1, 6, size=5).tolist()
dynamic_coords_x = np.random.randint(0, 100, size=5).tolist()
dynamic_coords_y = np.random.randint(0, 100, size=5).tolist()
return list(zip(dynamic_coords_x, dynamic_coords_y, dynamic_demands, dynamic_ready_time, dynamic_due_time, dynamic_service_time))
# ------------------ 第二阶段:添加动态客户后重新规划路径 ------------------
def reoptimize_with_dynamic_customers(data_initial, manager_initial, routing_initial, solution_initial):
# 获取每辆车在第30分钟时的位置
current_indices = []
for vehicle_id in range(data_initial['num_vehicles']):
index = routing_initial.Start(vehicle_id)
while not routing_initial.IsEnd(index):
time_var = routing_initial.GetDimensionOrDie('Time').CumulVar(index)
arrival_time = solution_initial.Value(time_var)
if arrival_time >= 30:
break
prev_index = index
index = solution_initial.Value(routing_initial.NextVar(index))
current_indices.append(prev_index) # 停留在最后一个小于30分钟的节点
# 合并静态+动态客户数据
dynamic_data = generate_dynamic_customers()
full_coords_x = [customers_df.iloc[i, 1] for i in range(num_static_customers)] + [d[0] for d in dynamic_data]
full_coords_y = [customers_df.iloc[i, 2] for i in range(num_static_customers)] + [d[1] for d in dynamic_data]
full_demands = demands + [d[2] for d in dynamic_data]
full_ready_time = ready_time + [d[3] for d in dynamic_data]
full_due_time = due_time + [d[4] for d in dynamic_data]
full_service_time = service_time + [d[5] for d in dynamic_data]
# 构建新的距离矩阵
n_total = num_static_customers + 5
new_distance_matrix = np.zeros((n_total, n_total), dtype=int)
for i in range(n_total):
for j in range(n_total):
dx = full_coords_x[i] - full_coords_x[j]
dy = full_coords_y[i] - full_coords_y[j]
new_distance_matrix[i][j] = int(np.hypot(dx, dy))
# 构造新的模型
data = {
'distance_matrix': new_distance_matrix,
'demands': full_demands,
'ready_time': full_ready_time,
'due_time': full_due_time,
'service_time': full_service_time,
'num_vehicles': num_vehicles,
'vehicle_capacities': vehicle_capacities,
'depot': depot,
'current_indices': current_indices,
'x_coords': full_coords_x,
'y_coords': full_coords_y
}
manager = pywrapcp.RoutingIndexManager(n_total, num_vehicles, depot)
routing = pywrapcp.RoutingModel(manager)
# 距离回调函数
def distance_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return data['distance_matrix'][from_node][to_node]
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
# 容量维度
def demand_callback(from_index):
from_node = manager.IndexToNode(from_index)
return data['demands'][from_node]
demand_callback_index = routing.RegisterUnaryTransitCallback(demand_callback)
routing.AddDimensionWithVehicleCapacity(
demand_callback_index, 0, data['vehicle_capacities'], True, 'Capacity'
)
# 时间维度
def time_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
travel_time = data['distance_matrix'][from_node][to_node]
return travel_time + data['service_time'][from_node]
time_callback_index = routing.RegisterTransitCallback(time_callback)
routing.AddDimension(
time_callback_index,
2000, # 最大等待时间
2000, # 最大行程时间
False, # 不允许等待
'Time'
)
time_dimension = routing.GetDimensionOrDie('Time')
for location_idx in range(len(data['distance_matrix'])):
index = manager.NodeToIndex(location_idx)
if location_idx != data['depot']:
time_dimension.CumulVar(index).SetRange(
data['ready_time'][location_idx],
data['due_time'][location_idx]
)
# 设置初始路径(固定已行驶部分)
fixed_assignment = routing.ReadAssignmentFromRoutes(current_indices, True)
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
)
search_parameters.time_limit.FromSeconds(10)
# 求解问题并获取 solution_final
solution = routing.SolveWithParameters(search_parameters)
print("✅ 重新规划路径(含动态客户)完成")
# 返回包括 solution 的完整数据
return data, manager, routing, solution
def plot_solution(data, manager, routing, solution, title="VRPTW Solution Visualization"):
"""绘制路径图并编号非空路径,跳过距离为 0 的车辆"""
plt.figure(figsize=(12, 8))
x_coords = data['x_coords'] # 所有客户点的 x 坐标(含动态客户)
y_coords = data['y_coords'] # 所有客户点的 y 坐标(含动态客户)
# 绘制所有客户点
for i in range(len(x_coords)):
if i == data['depot']:
plt.plot(x_coords[i], y_coords[i], 'rD', markersize=10, label='Depot')
else:
plt.plot(x_coords[i], y_coords[i], 'ko', markersize=5)
cmap = plt.get_cmap('tab20')
colors = [cmap(i) for i in range(20)]
total_distance = 0
path_count = 1 # 路径编号计数器,仅对非空路径编号
for vehicle_id in range(data['num_vehicles']):
index = routing.Start(vehicle_id)
path_x = []
path_y = []
path_nodes = []
while not routing.IsEnd(index):
node = manager.IndexToNode(index)
path_nodes.append(node)
path_x.append(x_coords[node])
path_y.append(y_coords[node])
index = solution.Value(routing.NextVar(index))
# 添加终点
node = manager.IndexToNode(index)
path_nodes.append(node)
path_x.append(x_coords[node])
path_y.append(y_coords[node])
# 计算该车辆路径的总距离
route_distance = 0
for i in range(len(path_nodes) - 1):
from_node = path_nodes[i]
to_node = path_nodes[i + 1]
route_distance += data['distance_matrix'][from_node][to_node]
total_distance += route_distance
# ✅ 如果行驶距离为 0,不绘制该车辆路径
if route_distance == 0:
continue
color = colors[vehicle_id % len(colors)]
# 绘制路径
plt.plot(path_x, path_y, '-', color=color, linewidth=1.5, alpha=0.7,
label=f'Route {path_count} ({route_distance})')
# 在路径起点附近添加编号
plt.text(path_x[0] + 1, path_y[0], str(path_count), fontsize=10, color=color)
path_count += 1
plt.title(title)
plt.xlabel("X Coordinate")
plt.ylabel("Y Coordinate")
plt.grid(True)
# 图例去重
handles, labels = plt.gca().get_legend_handles_labels()
by_label = dict(zip(labels, handles))
plt.legend(by_label.values(), by_label.keys())
plt.show()
print(f"Total Distance of All Routes: {total_distance}")
# ------------------ 主程序 ------------------
if __name__ == '__main__':
data_initial, manager_initial, routing_initial, solution_initial = solve_initial_problem()
data_final, manager_final, routing_final, solution_final = reoptimize_with_dynamic_customers(data_initial, manager_initial, routing_initial, solution_initial)
plot_solution(data_final, manager_final, routing_final, solution_final, title="Re-optimized with Dynamic Customers")
Traceback (most recent call last):
File "C:\Users\86158\PycharmProjects\PythonProject2\templates\prob2.py", line 302, in <module>
data_final, manager_final, routing_final, solution_final = reoptimize_with_dynamic_customers(data_initial, manager_initial, routing_initial, solution_initial)
File "C:\Users\86158\PycharmProjects\PythonProject2\templates\prob2.py", line 202, in reoptimize_with_dynamic_customers
fixed_assignment = routing.ReadAssignmentFromRoutes(current_indices, True)
File "C:\Users\86158\PycharmProjects\PythonProject2\.venv\lib\site-packages\ortools\constraint_solver\pywrapcp.py", line 5788, in ReadAssignmentFromRoutes
return _pywrapcp.RoutingModel_ReadAssignmentFromRoutes(self, routes, ignore_inactive_indices)
TypeError: Expecting a sequence