当前位置:   article > 正文

论文代码复现|并行无人机的调度优化问题PDSTSP

pdstsp

本文复现的是论文【1】的第二部分PDSTSP问题的求解:The flying sidekick traveling salesman problem: Optimization of drone-assisted parcel delivery - 百度学术 (baidu.com)

 本文的代码复现参考了师兄的这篇帖子:

论文代码复现 | 无人机与卡车联合配送(Python+Gurobi)(The flying sidekick traveling salesman problem)_HsinglukLiu的博客-CSDN博客

因为在本论文中作者提出了两个模型,一是有无人机辅助的旅行商(The flying sidekick traveling salesman problem)问题(简记:FSTSP),二是并行无人机的调度优化问题(Parallel drone scheduling TSP, 简记为:PDSTSP)。这两类问题都相当于利用无人机的灵活配送效率高的优势进行建模,论文构建了两类问题的数学规划模型。

简单理解第一种建模FSTSP问题是将无人机和卡车在配送过程中进行协同,如下图【Fig.3】所示,无人机是从卡车在配送过程中的顾客点进行起降,同时在论文【1】中,为了简化问题,无人机路径只能和卡车路径组成三角形,即在卡车连续服务的两个顾客间无人机必须完成顾客服务的往返,例如图中的【4-7-6】和【6-1-5】。

 第二种建模方式PDSTSP问题是指无人机和卡车彼此并行作业,当任务分配完成后,即不再有无人机和卡车的交互,见下图【Fig.2】。例如在图Fig.2(a)中的路径时间明显大于图b和图c,该问题相当于在初始给定一定量的顾客后我们进行两个决策,第一是哪些顾客交由无人机进行服务,哪些顾客交由卡车服务,第二步,在给定的顾客分配下,确定无人机和卡车的最优服务的顺序和路径。本文正是复现的PDSTSP问题的代码。

 本文复现了PDSTSP三部分的代码,首先是直接调用求解器Gurobi求解论文的数学规划模型:

代码实现:

  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Mon Jan 17 11:05:25 2022
  4. Target: solve PDSTSP by call GRB
  5. @author: wenpeng
  6. """
  7. from __future__ import print_function
  8. from gurobipy import *
  9. import re
  10. import math
  11. import matplotlib.pyplot as plt
  12. # import random
  13. import numpy as np
  14. # import copy
  15. import datetime
  16. import pandas as pd
  17. class Data:
  18. customerNum = 0
  19. nodeNum = 0
  20. UAVrange = 0
  21. cor_X = []
  22. cor_Y = []
  23. UAV_eligible_num = 0
  24. disMatrix = np.array([[]])
  25. class Customer:
  26. idNum: int
  27. x_cor: float
  28. y_cor: float
  29. withinRange: bool
  30. #################################
  31. path = 'c101.txt' #
  32. customerNum1 = 9 #
  33. UAVnum = 1 #
  34. #################################
  35. Customers = [Customer() for i in range(customerNum1)]
  36. # function to read data from .txt files
  37. def readData(data, path, customerNum):
  38. data.customerNum = customerNum
  39. data.nodeNum = customerNum+2
  40. f = open(path, 'r')
  41. lines = f.readlines()
  42. count = 0
  43. countCus = 0
  44. # read the info to our variables
  45. for line in lines:
  46. count = count + 1
  47. if(count == 2):
  48. line = line[:-1]
  49. str = re.split(r" +", line)
  50. data.UAVrange = float(str[0])
  51. elif(count >= 9 and count <= 9 + customerNum):
  52. line = line[:-1]
  53. str = re.split(r" +", line)
  54. data.cor_X.append(float(str[2]))
  55. data.cor_Y.append(float(str[3]))
  56. if(count > 9 and count <= 9 + customerNum):
  57. countCus = countCus +1
  58. Customers[countCus-1].idNum = int(str[1])
  59. Customers[countCus-1].x_cor = float(str[2])
  60. Customers[countCus-1].y_cor = float(str[3])
  61. data.cor_X.append(data.cor_X[0])
  62. data.cor_Y.append(data.cor_Y[0])
  63. # Compute the diatance matrix
  64. data.disMatrix = [([0] * data.nodeNum) for p in range(data.nodeNum)]
  65. # 初试化距离矩阵的维度,防止浅拷贝
  66. for i in range(0, data.nodeNum):
  67. for j in range(0, data.nodeNum):
  68. temp = (data.cor_X[i] - data.cor_X[j])**2 + (data.cor_Y[i] - data.cor_Y[j])**2
  69. data.disMatrix[i][j] = math.sqrt(temp)
  70. temp = 0
  71. return data
  72. def printData(data,customerNum):
  73. for i in range(data.customerNum):
  74. if(data.disMatrix[i+1][0] <= data.UAVrange):
  75. Customers[i].withinRange = 1
  76. else:
  77. Customers[i].withinRange = 0
  78. for l in range(data.customerNum):
  79. if(Customers[l].withinRange == 1):
  80. data.UAV_eligible_num = data.UAV_eligible_num + 1
  81. print(" ***********Data Info***********\n")
  82. print(" UAV range = %4d" %data.UAVrange)
  83. print(" Customer's number = %4d" %customerNum1)
  84. print(" UAV's eligible CusNum = %4d\n" %data.UAV_eligible_num)
  85. print("*****************************Distance Matrix***************************")
  86. for i in range(data.nodeNum):
  87. for j in range(data.nodeNum):
  88. print("%5.2f" %(data.disMatrix[i][j]), end = " ")
  89. print()
  90. print()
  91. class Solution:
  92. ObjVal = 0
  93. X = [[]]
  94. Y = [[]]
  95. U = []
  96. # z:float
  97. route_Truck = []
  98. route_UAV = [[]]
  99. def getSolution(self, data, model):
  100. solution = Solution()
  101. solution.ObjVal = model.ObjVal
  102. solution.X = [([0] * data.nodeNum) for j in range(data.nodeNum)]
  103. solution.Y = [([0] * UAVnum) for v in range(customerNum1)]
  104. solution.U = [[0] for i in range(data.nodeNum)]
  105. solution.z = 0
  106. # a = U[0].x
  107. for m in model.getVars():
  108. str = re.split(r"_", m.varName)
  109. if(str[0] == "X" and m.x ==1):
  110. solution.X[int(str[1])][int(str[2])] = m.x
  111. print(str, end="")
  112. print(" = %d" %m.x)
  113. elif(str[0] == 'Y' and m.x == 1):
  114. solution.Y[int(str[2])] = m.x
  115. elif(str[0] == 'U' and m.x > 0):
  116. solution.U[int(str[1])] = m.x
  117. elif(str[0] == 'z' and m.x >0):
  118. solution.z = m.x
  119. # get the route of truck and UAV
  120. j = 0
  121. for i in range(data.nodeNum):
  122. i = j # note that the variable is whether is alocal variable or a globle variable
  123. for j in range(data.nodeNum):
  124. if(solution.X[i][j] == 1):
  125. solution.route_Truck.append(i)
  126. print(" %d -" % i, end = " ")
  127. break
  128. print(" 0")
  129. solution.route_Truck.append(0)
  130. print("\n\n --------- Route of UAV ---------")
  131. # count = 0
  132. for v in range(UAVnum):
  133. solution.route_UAV.append([0])
  134. print(" 0 ", end = "")
  135. for i in range(customerNum1):
  136. if(solution.Y[i] == 1):
  137. solution.route_UAV[v].append(i+1)
  138. print("- %d -" %(i+1), end = " ")
  139. print("0 ", end = " ")
  140. return solution
  141. #Reading data
  142. data = Data()
  143. # uavSpeed = 2.0
  144. # truckSpeed = 1.0
  145. readData(data, path, customerNum1)
  146. printData(data, customerNum1)
  147. # =========================Build the model=======================
  148. big_M = 10000
  149. # construct the model object
  150. model = Model("PDSTSP_by_Gurobi")
  151. # Initialize variables
  152. # create variables: Muiti-dimension vector: from inner to outer
  153. # X_ij hat
  154. X = [[[] for i in range(data.nodeNum)] for j in range(data.nodeNum)]
  155. # Y_iv hat
  156. Y = [[[] for v in range(customerNum1+1)] for i in range(UAVnum)]
  157. # U_i
  158. U = [[] for i in range(data.nodeNum)]
  159. # z
  160. z: float
  161. for i in range(data.nodeNum):
  162. name1 = 'U_' + str(i)
  163. U[i] = model.addVar(0, data.nodeNum, vtype = GRB.CONTINUOUS, name = name1)
  164. for j in range(data.nodeNum):
  165. name2 = 'X_' + str(i) + "_" + str(j)
  166. X[i][j] = model.addVar(0, 1, vtype = GRB.BINARY, name = name2)
  167. # for i in range(1, customerNum1+1):
  168. for v in range(UAVnum):
  169. for k in range(customerNum1):
  170. name3 = 'Y_' + str(v) + '_' + str(k)
  171. Y[v][k] = model.addVar(0, 1, vtype = GRB.BINARY, name = name3)
  172. z=model.addVar(0, big_M, vtype = GRB.CONTINUOUS, name = 'z')
  173. # Add contraints
  174. # create the objective expressive(1)
  175. obj = LinExpr(0)
  176. # add thee objective funtion into the model
  177. model.setObjective(z, GRB.MINIMIZE)
  178. # constraint (1)
  179. expr = LinExpr(0)
  180. for i in range(data.nodeNum-1):
  181. for j in range(1, data.nodeNum):
  182. if (i != j):
  183. expr.addTerms(data.disMatrix[i][j], X[i][j])
  184. model.addConstr(expr <= z, 'c1')
  185. expr.clear()
  186. # constraint (2)
  187. expr = LinExpr(0)
  188. for v in range(UAVnum):
  189. expr = LinExpr(0)
  190. expr.addTerms(data.disMatrix[0][0], Y[v][0])
  191. for i in range(1, customerNum1+1):
  192. expr.addTerms(data.disMatrix[0][i], Y[v][i-1])
  193. model.addConstr(expr <= z , 'c2')
  194. expr.clear()
  195. # constrait (3)
  196. expr = LinExpr(0)
  197. for j in range(1,data.nodeNum-1):
  198. for i in range(data.nodeNum-1):
  199. if(i!=j):
  200. expr.addTerms(1, X[i][j])
  201. for v in range(UAVnum):
  202. if(Customers[j-1].withinRange == 1):
  203. expr.addTerms(1, Y[v][j-1])
  204. model.addConstr(expr == 1, 'c3')
  205. expr.clear()
  206. # constraint (4)
  207. expr = LinExpr(0)
  208. for j in range(1, data.nodeNum):
  209. expr.addTerms(1, X[0][j])
  210. model.addConstr(expr == 1, 'c4')#其中包括了depot到depot的弧
  211. expr.clear()
  212. #constraint (5)
  213. expr = LinExpr(0)
  214. for i in range(data.nodeNum-1):
  215. expr.addTerms(1, X[i][customerNum1+1])
  216. model.addConstr(expr == 1, 'c5')
  217. expr.clear()
  218. #constraint (6)
  219. for j in range(1, customerNum1+1):
  220. expr1 = LinExpr(0)
  221. expr2 = LinExpr(0)
  222. for i in range(customerNum1+1):
  223. if(i!=j):
  224. expr1.addTerms(1, X[i][j])
  225. for k in range(1, data.nodeNum):
  226. if(k!=j):
  227. expr2.addTerms(1, X[j][k])
  228. model.addConstr(expr1 == expr2, 'c6')
  229. expr1.clear()
  230. expr2.clear()
  231. # constraint (7)
  232. for i in range(1, customerNum1+1):
  233. for j in range(1, customerNum1+2):
  234. if(i!=j):
  235. model.addConstr(U[i]-U[j]+1<=(data.nodeNum)*(1-X[i][j]),'c7')
  236. # solve the problem
  237. model.write('b.lp')
  238. model.Params.timelimit = 7200*6
  239. model.optimize()
  240. # get the solution info
  241. solution1 = Solution()
  242. solution = solution1.getSolution(data, model)
  243. print("\n\n\n-----optimal value-----")
  244. print("Obj: %g" % solution.ObjVal)
  245. print("\n\n ------Route of Truck------")
  246. j = 0
  247. for i in range(data.nodeNum):
  248. i = j
  249. for j in range(data.nodeNum):
  250. if(solution.X[i][j] == 1):
  251. print(" %d -" % i, end = " ")
  252. break
  253. print(" 0")
  254. print("\n\n -------- Route of UAV --------")
  255. for v in range(UAVnum):
  256. print("UAV-%d :"%v, end = " ")
  257. for j in range(customerNum1):
  258. if(solution.Y[j] == 1):
  259. print(" %d -" %(j+1), end = " ")
  260. print()
  261. # draw the route graph
  262. # draw all the nodes first
  263. # data1 = Data()
  264. # readData(data1, path, 100)
  265. fig = plt.figure(figsize=(15,10))
  266. font_dict = {'family': 'Arial', # serif
  267. 'style': 'normal', # 'italic',
  268. 'weight': 'normal',
  269. 'color': 'darkred',
  270. 'size': 30,
  271. }
  272. font_dict2 = {'family': 'Arial', # serif
  273. 'style': 'normal', # 'italQic',
  274. 'weight': 'normal',
  275. 'color': 'darkred',
  276. 'size': 24,
  277. }
  278. plt.xlabel('x', font_dict)
  279. plt.ylabel('y', font_dict)
  280. plt.title('Optimal Solution for PDSTSP by Gurobi', font_dict)
  281. plt.xticks(fontsize=22)
  282. plt.yticks(fontsize=22) # plt.yticks(fontsize=30)
  283. plt.grid(True, color='r', linestyle='-', linewidth=2)
  284. '''
  285. marker='o'
  286. marker=','
  287. marker='.'
  288. marker=(9, 3, 30)
  289. marker='+'
  290. marker='v'
  291. marker='^'
  292. marker='<'
  293. marker='>'
  294. marker='1'
  295. marker='2'
  296. marker='3'
  297. red blue green
  298. '''
  299. plt.scatter(data.cor_X[0], data.cor_Y[0], c='blue', alpha=1, marker=',', linewidths=5, label='depot')
  300. plt.scatter(data.cor_X[1:-1], data.cor_Y[1:-1], c='magenta', alpha=1, marker='o', linewidths=5, label='customer')
  301. # Draw the route
  302. for i in range(data.nodeNum):
  303. for j in range(data.nodeNum):
  304. if(solution.X[i][j] == 1):
  305. x = [data.cor_X[i], data.cor_X[j]]
  306. y = [data.cor_Y[i], data.cor_Y[j]]
  307. plt.plot(x, y, 'b', linewidth = 3)
  308. plt.text(data.cor_X[i]-0.2, data.cor_Y[i], str(i), fontdict = font_dict2)
  309. for v in range(UAVnum):
  310. for j in range(customerNum1):
  311. if(solution.Y[j] == 1):
  312. x = [data.cor_X[0], data.cor_X[j+1]]
  313. y = [data.cor_Y[0], data.cor_Y[j+1]]
  314. plt.plot(x, y, 'r--', linewidth = 3)
  315. plt.text(data.cor_X[j+1]-0.2, data.cor_Y[j+1], str(j+1), fontdict = font_dict2)
  316. # plt.frid(True)
  317. plt.grid(False)
  318. plt.legend(loc='best', fontsize = 20)
  319. plt.show()

第二是对论文中提出的PDSTSP启发式伪代码进行Python实现,第三部分的工作是比较不同TSP求解方法对PDSTSP启发式方法求解质量和效率的影响。其中我们谈到的PDSTSP启发式方法是指下面的伪代码【包含Alorithm6和Algorithm7】:

简单理解PDSTSP heuristic的算法6和算法7如下:首先从已知的所有顾客中分辩出可以由无人机进行服务的顾客(主要指在无人机的电池耐力范围内的顾客)和只能由卡车进行服务的顾客(顾客的需求重量超出无人机的服务载荷,或需要顾客进行签字或者当面操作的情况),在初始化时将所有无人机可以服务的顾客全部分派给无人机,剩余不能由无人机服务的顾客分派给卡车,这样分派给无人机的顾客用求解PMS的算法进行安排最优服务顺序和路径(记为问题A,后面再展开),分派给卡车的顾客调用求解TSP问题的算法求解(记为问题B,同A,后面展开),通过比较问题A和B的服务时间,最大者即为原PDSTSP问题的解。

当然这样的初始解通常都有很大的优化空间:例如在论文【1】中,作者设置了不同比例80%,90%的顾客可以由无人机服务,这就意味着服务的主要服务载荷的loading在无人机,意味着无人机服务的时长通常会大于卡车的服务时长,这样进行第一步PDSTSP启发式的优化操作:将在无人机上的被服务的顾客安排到卡车上,那么选择哪一个顾客做这样的移动呢?答案是进行遍历所有无人机上的顾客,放到卡车上之后使原问题(PDSTSP问题本身)目标函数值最小(也就是最优)的移动方案,这样就完成了一次移动,重复这样的过程,无人机上的顾客数量在逐渐减少,卡车的服务顾客在逐渐增加,这样原来由无人机主导的服务时间会逐渐降低,卡车的服务时间会逐渐增加,最后无人机上顾客服务时间会在某一位顾客从无人机上移动到卡车上之后,卡车的服务时间大于无人机的服务时间,这时第一阶段的优化结束。

进行第二阶段PDSTSP的优化,即算法7: 将现在在卡车上的能被无人机服务的顾客再和现在在无人机上服务的顾客进行交换,如果依然可以实现当前的目标函数值的优化,就继续进行,直到没有可以继续优化的空间,目标函数值保持不变,算法结束。

在这部分的实现中,有两个点没有展开,就是问题A和B的求解:这两个问题也是运筹学中的经典问题,第一个是Parallel Machine Scheduling (PMS) problem给定无人机服务顾客进行任务调度优化的问题,论文中分享了两篇求解的论文,有兴趣的同学可以查阅参考文献【2】【3】,这里简单讲一下只有一架无人机的场景,相当于PMS问题的机台只有一台,那问题就退化为所有客户提供服务的时间总和即为问题的目标函数值。并且顾客的先后服务顺序不会影响最后的PMS的目标函数值。第二个是求解Traveling Salesman Problem(TSP),这个运筹学中的经典问题可以用多种方法来求解,本文分别使用Gurobi和模拟退火(Simulated Annealing)算法进行了尝试。代码如下:

Gurobi解TSP的PDSTSP启发式实现:

  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Thu Jan 6 13:41:33 2022
  4. @ Weihui, Henan, China
  5. @ author: wenpeng
  6. @ PDSTSP heuristic: algorithm 6 and algorithm 7
  7. @ solveTSP: Called Gurobi to solve IP formaulation-TSP
  8. """
  9. from gurobipy import *
  10. import re
  11. import math
  12. import matplotlib.pyplot as plt
  13. import random
  14. import numpy as np
  15. import copy
  16. import datetime
  17. #import pandas as pd
  18. class Data:
  19. customerNum = 0
  20. nodeNum = 0
  21. UAVrange = 0
  22. cor_X = []
  23. cor_Y = []
  24. UAV_eligible_num = 0
  25. disMatrix = np.array([[]])
  26. class Customer:
  27. idNum: int
  28. x_cor: float
  29. y_cor: float
  30. withinRange: bool
  31. path = 'c101.txt'
  32. customerNum1 = 5
  33. Customers = [Customer() for i in range(customerNum1)]
  34. # function to read data from .txt files
  35. def readData(data, path, customerNum):
  36. data.customerNum = customerNum
  37. data.nodeNum = customerNum+2
  38. f = open(path, 'r')
  39. lines = f.readlines()
  40. count = 0
  41. countCus = 0
  42. # read the info to our variables
  43. for line in lines:
  44. count = count + 1
  45. if(count == 2):
  46. line = line[:-1]
  47. str = re.split(r" +", line)
  48. data.UAVrange = float(str[0])
  49. elif(count >= 9 and count <= 9 + customerNum):
  50. line = line[:-1]
  51. str = re.split(r" +", line)
  52. data.cor_X.append(float(str[2]))
  53. data.cor_Y.append(float(str[3]))
  54. if(count > 9 and count <= 9 + customerNum):
  55. countCus = countCus +1
  56. Customers[countCus-1].idNum = int(str[1])
  57. Customers[countCus-1].x_cor = float(str[2])
  58. Customers[countCus-1].y_cor = float(str[3])
  59. data.cor_X.append(data.cor_X[0])
  60. data.cor_Y.append(data.cor_Y[0])
  61. # Compute the diatance matrix
  62. data.disMatrix = [([0] * data.nodeNum) for p in range(data.nodeNum)]
  63. # 初试化距离矩阵的维度,防止浅拷贝
  64. for i in range(0, data.nodeNum):
  65. for j in range(0, data.nodeNum):
  66. temp = (data.cor_X[i] - data.cor_X[j])**2 + (data.cor_Y[i] - data.cor_Y[j])**2
  67. data.disMatrix[i][j] = math.sqrt(temp)
  68. temp = 0
  69. return data
  70. def printData(data,customerNum):
  71. for i in range(data.customerNum):
  72. if(data.disMatrix[i+1][0] <= data.UAVrange):
  73. Customers[i].withinRange = 1
  74. else:
  75. Customers[i].withinRange = 0
  76. for l in range(data.customerNum):
  77. if(Customers[l].withinRange == 1):
  78. data.UAV_eligible_num = data.UAV_eligible_num + 1
  79. print(" ***********Data Info***********\n")
  80. print(" UAV range = %4d" %data.UAVrange)
  81. print(" Customer's number = %4d" %customerNum1)
  82. print(" UAV's eligible CusNum = %4d\n" %data.UAV_eligible_num)
  83. # print("*****************************Distance Matrix***************************")
  84. # for i in range(data.nodeNum):
  85. # for j in range(data.nodeNum):
  86. # print("%5.2f" %(data.disMatrix[i][j]), end = " ")
  87. # print()
  88. # print()
  89. #Reading data
  90. data = Data()
  91. uavSpeed = 2.0
  92. truckSpeed = 1.0
  93. readData(data, path, customerNum1)
  94. printData(data, customerNum1)
  95. # plt.scatter(data.cor_X[0], data.cor_Y[0], c='blue', alpha=1, marker=',', linewidths=5, label='depot')
  96. # plt.scatter(data.cor_X[1:-1], data.cor_Y[1:-1], c='magenta', alpha=1, marker='o', linewidths=5, label='customer')
  97. def solvePMS(cuss):
  98. # print("Function solvePMS() was called\n")
  99. uavMkspn = 0
  100. tempuavAssignments = []
  101. for i in range(len(cuss)):
  102. uavMkspn = uavMkspn + 2 * 0.5 * data.disMatrix[0][cuss[i].idNum]
  103. tempuavAssignments.append(cuss[i].idNum)
  104. uavAssignments = copy.deepcopy(tempuavAssignments)
  105. return [uavMkspn, uavAssignments]
  106. # def tsp_new_path(oldpath):
  107. # #change oldpath to its neighbor
  108. # N = len(oldpath)
  109. # if(random.random() < 0.25): # generate two positions and change them
  110. # chpos = random.sample(range(N),2)
  111. # newpath = copy.deepcopy(oldpath)
  112. # if(chpos[0] == chpos[1]):
  113. # newpath = tsp_new_path(oldpath)
  114. # newpath[chpos[1]] = oldpath[chpos[0]]
  115. # newpath[chpos[0]] = oldpath[chpos[1]]
  116. # else: # generate three place and change a-b & b-c
  117. # d = random.sample(range(N),3)
  118. # d.sort()
  119. # a = d[0]
  120. # b = d[1]
  121. # c = d[2]
  122. # if (a != b and b!=c):
  123. # newpath = copy.deepcopy(oldpath)
  124. # newpath[a:(c+1)] = oldpath[b:(c+1)] + oldpath[a:b]
  125. # else:
  126. # newpath = tsp_new_path(oldpath)
  127. # # print("Newpath:*********************")
  128. # # print(newpath)
  129. # return newpath
  130. def tsp_len(dis, path):#path format: < 8 5 7 6 >
  131. # dis: N*N adjcent matrix
  132. # verctor length is N 1
  133. NN1 = len(path)
  134. leng = 0
  135. if(NN1 == 1):
  136. leng = 2*dis[0][1]
  137. else:
  138. for i in range(NN1-1): # 0 1 2 3 ... 9
  139. leng = leng + data.disMatrix[path[i]][path[i+1]]
  140. leng = leng + data.disMatrix[0][path[NN1-1]]
  141. leng = leng + data.disMatrix[0][path[0]]
  142. return leng
  143. def getValue(var_dict, nodeNumm):
  144. x_value = np.zeros([nodeNumm + 1, nodeNumm + 1])
  145. for key in var_dict.keys():
  146. a = key[0]
  147. b = key[1]
  148. x_value[a][b] = var_dict[key].x
  149. return x_value
  150. def getRoute(x_value):
  151. x = copy.deepcopy(x_value)
  152. previousPoint = 0
  153. route_temp = [previousPoint]
  154. count = 0
  155. while(len(route_temp) < len(x)):
  156. if(x[previousPoint][count] > 0.99):
  157. previousPoint = count
  158. route_temp.append(previousPoint)
  159. count = 0
  160. continue
  161. else:
  162. count += 1
  163. return route_temp
  164. def solveTSP(truckcuss):
  165. # print("Function solveTSP() was called\n")
  166. global solveTSPcalledTime
  167. solveTSPcalledTime += 1
  168. truckMkspn = 0
  169. truckRoute = []
  170. x0 = [0]
  171. backuppath = []
  172. for i in range(len(truckcuss)):
  173. x0.append(truckcuss[i].idNum)
  174. backuppath.append(truckcuss[i].idNum)
  175. x0.append(0)
  176. # if(solveTSPcalledTime > 75):
  177. # print("stop for check")
  178. N = len(x0)-1
  179. cost = [([0] * (N+1)) for p in range(N+1)]
  180. for i in range(N+1):
  181. for j in range(N+1):
  182. if(i != j):
  183. cost[i][j] = data.disMatrix[x0[i]][x0[j]]
  184. # if(solveTSPcalledTime > 75):
  185. # print("stop for check")
  186. # cost = data.disMatrix
  187. if(len(truckcuss)<3):
  188. # print('return function was called, which means the num of truck route=2')
  189. x0 = x0[1:N]
  190. return [tsp_len(cost, x0), x0]
  191. model = Model('TSPbyGRB')
  192. model.setParam('OutputFlag', 0)
  193. X = {}
  194. mu = {}
  195. for i in range(N+1):
  196. mu[i] = model.addVar(lb = 0.0
  197. , ub = 100
  198. , vtype = GRB.CONTINUOUS
  199. , name = "mu_" + str(i))
  200. for j in range(N+1):
  201. if(i != j):
  202. X[i, j] = model.addVar(vtype = GRB.BINARY
  203. , name = 'x_' + str(i)+'_'+str(j)
  204. )
  205. # set objective function
  206. obj = LinExpr(0)
  207. for key in X.keys():
  208. i = key[0]
  209. j = key[1]
  210. if(i < N and j < N):
  211. obj.addTerms(cost[key[0]][key[1]], X[key])
  212. elif(i == N):
  213. obj.addTerms(cost[0][key[1]], X[key])
  214. elif(j == N):
  215. obj.addTerms(cost[key[0]][0], X[key])
  216. model.setObjective(obj, GRB.MINIMIZE)
  217. # add constraints 1
  218. for j in range(1, N+1):
  219. lhs = LinExpr(0)
  220. for i in range(0, N):
  221. if(i!=j):
  222. lhs.addTerms(1, X[i, j])
  223. model.addConstr(lhs == 1, name = 'visit_' + str(j))
  224. # add constraint 2
  225. for i in range(0, N):
  226. lhs = LinExpr(0)
  227. for j in range(1, N + 1):
  228. if(i != j):
  229. lhs.addTerms(1, X[i, j])
  230. model.addConstr(lhs == 1, name = 'visit_' + str(j))
  231. for i in range(0, N):
  232. for j in range(1, N+1):
  233. if(i != j):
  234. model.addConstr(mu[i] - mu[j] + 100*X[i,j] <= 100-1)
  235. model.optimize()
  236. # if(solveTSPcalledTime > 75):
  237. # print("stop for check")
  238. x_value = getValue(X, N)
  239. truckRoute1 = getRoute(x_value)
  240. truckRoute1 = truckRoute1[1:N]
  241. for i in range(len(truckRoute1)):
  242. truckRoute.append(backuppath[truckRoute1[i]-1])
  243. truckMkspn = tsp_len(cost, truckRoute)
  244. return [truckMkspn, truckRoute]
  245. def swap(umk, tmk, ua, tr):
  246. # stand for: uavMkspn, truckMkspn, uavAssignments, truckRoute
  247. print("function SWAP was called, and uav Makespan, truck makespan, UAV customers and Truck customers' are: %.2f %.2f "%(umk,tmk) + str(ua) + str(tr))
  248. ms = 0 # maxSavings for return
  249. intersecCus = []
  250. for ii in range(len(tr)):
  251. if(Customers[tr[ii]-1].withinRange == 1):
  252. intersecCus.append(tr[ii])
  253. n1 = len(ua)
  254. n2 = len(intersecCus)
  255. for i in range(n1):
  256. for j in range(n2):
  257. tempuci = ua[i] # 备份无人机当前待交换顾客编号
  258. tempintersecj = intersecCus[j] # 备份卡车带交换顾客编号
  259. backupua = copy.deepcopy(ua)
  260. ua.remove(tempuci)
  261. ua.append(intersecCus[j])
  262. uaP = copy.deepcopy(ua)
  263. # resotore the origin status of UAV service
  264. ua = copy.deepcopy(backupua)
  265. backuptr = copy.deepcopy(tr)
  266. tr.append(tempuci)
  267. tr.remove(tempintersecj)
  268. trP = copy.deepcopy(tr)
  269. # restore the origin status of TRUCK service
  270. tr = copy.deepcopy(backuptr)
  271. uavCusP = []
  272. truckCusP = []
  273. for g in range(len(uaP)):
  274. uavCusP.append(Customers[uaP[g]-1])
  275. for w in range(len(trP)):
  276. truckCusP.append(Customers[trP[w]-1])
  277. # Variable initialization
  278. umkP = 0
  279. tmkP = 0
  280. uasP = []
  281. trP = []
  282. [umkP, uasP] = solvePMS(uavCusP)
  283. [tmkP, trP] = solveTSP(truckCusP)
  284. objnew = max([umkP, tmkP])
  285. objold = max([umk, tmk])
  286. if(objold - objnew > ms):
  287. ms = objold - objnew
  288. umkPrime = umkP
  289. tmkPrime = tmkP
  290. uasPrime = copy.deepcopy(uasP)
  291. trPrime = copy.deepcopy(trP)
  292. if(ms>0):
  293. umk = umkPrime
  294. tmk = tmkPrime
  295. ua = copy.deepcopy(uasPrime)
  296. tr = copy.deepcopy(trPrime)
  297. return [ms, umk, tmk, ua, tr]
  298. def PDSTSPheuristic(allcus):
  299. #Initialize
  300. uavCustomers = []
  301. truckCustomers = []
  302. for i in range(len(allcus)):
  303. if(allcus[i].withinRange == 1):
  304. uavCustomers.append(allcus[i])
  305. else:
  306. truckCustomers.append(allcus[i])
  307. uavMkspn1 = 0
  308. truckMkspn1 = 0
  309. uavAssignments1 = []
  310. truckRoute1 = []
  311. [uavMkspn1, uavAssignments1] = solvePMS(uavCustomers)
  312. [truckMkspn1, truckRoute1] = solveTSP(truckCustomers)
  313. while 1:
  314. if(uavMkspn1 > truckMkspn1):
  315. maxSavings = 0
  316. backupUAVcus = copy.deepcopy(uavAssignments1) # 备份无人机顾客的ID number
  317. countUAVCusNum = len(uavAssignments1)
  318. # iPrime =0
  319. uavMkspnPrime = 0
  320. truckMkspnPrime = 0
  321. for i in range(countUAVCusNum):
  322. uavAssignmentsP = [] # 定义无人机顾客顺序
  323. truckRouteP = [] # 定义卡车顾客服务顺序
  324. uavAssignmentsPP = [] #
  325. truckRoutePP = [] #
  326. uavCustomersP = [] # 无人机顾客
  327. truckCustomersP = [] # 卡车服务顾客
  328. # 依次添加<某个无人机访问顾客>到卡车路径中
  329. tempuavA = copy.deepcopy(uavAssignments1)
  330. uavAssignments1.remove(backupUAVcus[i])
  331. uavAssignmentsP = copy.deepcopy(uavAssignments1)
  332. temptrkA = copy.deepcopy(truckRoute1)
  333. truckRoute1.append(backupUAVcus[i])
  334. truckRouteP = copy.deepcopy(truckRoute1) #注意深度copy
  335. for g in range(len(uavAssignmentsP)):
  336. uavCustomersP.append(Customers[uavAssignments1[g]-1])
  337. for w in range(len(truckRouteP)):
  338. truckCustomersP.append(Customers[truckRoute1[w]-1])
  339. # 将调换后的顾客重新计算卡车和无人机最佳访问的最短路和顾客访问顺序
  340. [uavMkspnP, uavAssignmentsPP] = solvePMS(uavCustomersP)
  341. [truckMakespnP, truckRoutePP] = solveTSP(truckCustomersP)
  342. savings = uavMkspn1 - uavMkspnP
  343. cost = truckMakespnP - truckMkspn1
  344. if((savings - cost) > maxSavings):
  345. maxSavings = savings - cost
  346. # iPrime = i
  347. # print("i = %4d" %i, end = " ")
  348. uavMkspnPrime = uavMkspnP
  349. truckMkspnPrime = truckMakespnP
  350. uavAssignmentsPrime = copy.deepcopy(uavAssignmentsPP)
  351. truckRoutePrime = copy.deepcopy(truckRoutePP)
  352. # 做完所有计算后,恢复成计算初的状态
  353. uavAssignments1 = copy.deepcopy(tempuavA)
  354. truckRoute1 = copy.deepcopy(temptrkA)
  355. if(maxSavings > 0):
  356. print("maxSaving is : %6.2f" %maxSavings)
  357. uavAssignments1 = copy.deepcopy(uavAssignmentsPrime)
  358. truckRoute1 = copy.deepcopy(truckRoutePrime)
  359. uavMkspn1 = uavMkspnPrime
  360. truckMkspn1 = truckMkspnPrime
  361. else:
  362. [maxSavings, uavMkspn1, truckMkspn1, uavAssignments1,truckRoute1] = swap(uavMkspn1, truckMkspn1, uavAssignments1, truckRoute1)
  363. if(maxSavings == 0):
  364. break
  365. else:
  366. [maxSavings, uavMkspn1, truckMkspn1, uavAssignments1,truckRoute1] = swap(uavMkspn1, truckMkspn1, uavAssignments1, truckRoute1)
  367. if(maxSavings == 0):
  368. break
  369. print("PDSTSP heuristic (Algorithm6) was successfully called!")
  370. return [uavMkspn1, truckMkspn1, uavAssignments1, truckRoute1]
  371. if __name__ == "__main__":
  372. solveTSPcalledTime = 0
  373. uavMakespn = 0
  374. truckMakespn = 0
  375. uavAssign = []
  376. truckRoute = []
  377. starttime = datetime.datetime.now()
  378. [uavMakespn, truckMakespn, uavAssign, truckRoute] = PDSTSPheuristic(Customers)
  379. endtime = datetime.datetime.now()
  380. print('\n*************** The optimal solution are AS FOLLOWS: *************\n')
  381. print('UAV makespan : %5.2f' %(uavMakespn))
  382. print('Truck makespan: %5.2f' %(truckMakespn))
  383. print('UAV Assignments : ' + str(uavAssign))
  384. print('Truck Assignments: ' + str(truckRoute))
  385. print("solve TSP function called time: %d" %(solveTSPcalledTime))
  386. strrr="run time: %d seconds" % ((endtime - starttime).seconds)
  387. print(strrr)
  388. print('\n******* Detailed path info was shown in PLOTS windows above! *****')
  389. # draw the route graph
  390. # draw all the nodes first
  391. # data1 = Data()
  392. # readData(data1, path, 100)
  393. fig = plt.figure(figsize=(15,10))
  394. font_dict = {'family': 'Arial', # serif
  395. 'style': 'normal', # 'italic',
  396. 'weight': 'normal',
  397. 'color': 'darkred',
  398. 'size': 30,
  399. }
  400. font_dict2 = {'family': 'Arial', # serif
  401. 'style': 'normal', # 'italQic',
  402. 'weight': 'normal',
  403. 'color': 'darkred',
  404. 'size': 24,
  405. }
  406. plt.xlabel('x', font_dict)
  407. plt.ylabel('y', font_dict)
  408. plt.title('Optimal Solution for PDSTSP heuristic (GRB4TSP)', font_dict)
  409. plt.xticks(fontsize=22)
  410. plt.yticks(fontsize=22) # plt.yticks(fontsize=30)
  411. plt.grid(True, color='r', linestyle='-', linewidth=2)
  412. '''
  413. marker='o'
  414. marker=','
  415. marker='.'
  416. marker=(9, 3, 30)
  417. marker='+'
  418. marker='v'
  419. marker='^'
  420. marker='<'
  421. marker='>'
  422. marker='1'
  423. marker='2'
  424. marker='3'
  425. red blue green
  426. '''
  427. plt.scatter(data.cor_X[0], data.cor_Y[0], c='blue', alpha=1, marker=',', linewidths=5, label='depot')
  428. plt.scatter(data.cor_X[1:-1], data.cor_Y[1:-1], c='magenta', alpha=1, marker='o', linewidths=5, label='customer')
  429. # Drew the route
  430. lengthTR = len(truckRoute)
  431. for i in range(lengthTR-1):
  432. x = [Customers[truckRoute[i]-1].x_cor, Customers[truckRoute[i+1]-1].x_cor]
  433. y = [Customers[truckRoute[i]-1].y_cor, Customers[truckRoute[i+1]-1].y_cor]
  434. plt.plot(x, y, 'b', linewidth = 3)
  435. plt.text(Customers[truckRoute[i]-1].x_cor-0.2, Customers[truckRoute[i]-1].y_cor, str(truckRoute[i]), fontdict = font_dict2)
  436. # conect depot to the first customer
  437. # x = [data.cor_X[0], Customers[truckRoute[0]-1].x_cor]
  438. # y = [data.cor_Y[0], Customers[truckRoute[0]-1].y_cor]
  439. x = [data.cor_X[0], data.cor_X[truckRoute[0]]]
  440. y = [data.cor_Y[0], data.cor_Y[truckRoute[0]]]
  441. plt.plot(x, y, 'b', linewidth = 3)
  442. plt.text(data.cor_X[truckRoute[0]]-0.2, data.cor_Y[truckRoute[0]], str(truckRoute[0]), fontdict = font_dict2)
  443. # conect depot to the last customer
  444. x = [data.cor_X[0], data.cor_X[truckRoute[lengthTR-1]]]
  445. y = [data.cor_Y[0], data.cor_Y[truckRoute[lengthTR-1]]]
  446. plt.plot(x, y, 'b', linewidth = 3)
  447. plt.text(data.cor_X[truckRoute[lengthTR-1]]-0.2, data.cor_Y[truckRoute[lengthTR-1]], str(truckRoute[lengthTR-1]), fontdict = font_dict2)
  448. for i in range(len(uavAssign)):
  449. x = [data.cor_X[0], data.cor_X[uavAssign[i]]]
  450. y = [data.cor_Y[0], data.cor_Y[uavAssign[i]]]
  451. plt.plot(x, y, 'r--', linewidth = 3)
  452. plt.text(data.cor_X[uavAssign[i]]-0.2, data.cor_Y[uavAssign[i]], str(uavAssign[i]), fontdict=font_dict2)
  453. #plt.grid(True)
  454. plt.grid(False)
  455. plt.legend(loc='best', fontsize = 20)
  456. plt.show()

SA解TSP的PDSTSP启发式实现:

  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Thu Jan 6 13:41:33 2022
  4. @ Weihui, Henan, China
  5. @ author: wenpeng
  6. @ PDSTSP heuristic: algorithm 6 and algorithm 7
  7. @ solveTSP: Simulated annealing algorithm [1000, 20, 0.97, 100]
  8. """
  9. import re
  10. import math
  11. import matplotlib.pyplot as plt
  12. import random
  13. import numpy as np
  14. import copy
  15. import datetime
  16. #import pandas as pd
  17. class Data:
  18. customerNum = 0
  19. nodeNum = 0
  20. UAVrange = 0
  21. cor_X = []
  22. cor_Y = []
  23. UAV_eligible_num = 0
  24. disMatrix = np.array([[]])
  25. class Customer:
  26. idNum: int
  27. x_cor: float
  28. y_cor: float
  29. withinRange: bool
  30. path = 'c101.txt'
  31. customerNum1 = 100
  32. Customers = [Customer() for i in range(customerNum1)]
  33. # function to read data from .txt files
  34. def readData(data, path, customerNum):
  35. data.customerNum = customerNum
  36. data.nodeNum = customerNum+2
  37. f = open(path, 'r')
  38. lines = f.readlines()
  39. count = 0
  40. countCus = 0
  41. # read the info to our variables
  42. for line in lines:
  43. count = count + 1
  44. if(count == 2):
  45. line = line[:-1]
  46. str = re.split(r" +", line)
  47. data.UAVrange = float(str[0])
  48. elif(count >= 9 and count <= 9 + customerNum):
  49. line = line[:-1]
  50. str = re.split(r" +", line)
  51. data.cor_X.append(float(str[2]))
  52. data.cor_Y.append(float(str[3]))
  53. if(count > 9 and count <= 9 + customerNum):
  54. countCus = countCus +1
  55. Customers[countCus-1].idNum = int(str[1])
  56. Customers[countCus-1].x_cor = float(str[2])
  57. Customers[countCus-1].y_cor = float(str[3])
  58. data.cor_X.append(data.cor_X[0])
  59. data.cor_Y.append(data.cor_Y[0])
  60. # Compute the diatance matrix
  61. data.disMatrix = [([0] * data.nodeNum) for p in range(data.nodeNum)]
  62. # 初试化距离矩阵的维度,防止浅拷贝
  63. for i in range(0, data.nodeNum):
  64. for j in range(0, data.nodeNum):
  65. temp = (data.cor_X[i] - data.cor_X[j])**2 + (data.cor_Y[i] - data.cor_Y[j])**2
  66. data.disMatrix[i][j] = math.sqrt(temp)
  67. temp = 0
  68. return data
  69. def printData(data,customerNum):
  70. for i in range(data.customerNum):
  71. if(data.disMatrix[i+1][0] <= data.UAVrange):
  72. Customers[i].withinRange = 1
  73. else:
  74. Customers[i].withinRange = 0
  75. for l in range(data.customerNum):
  76. if(Customers[l].withinRange == 1):
  77. data.UAV_eligible_num = data.UAV_eligible_num + 1
  78. print(" ***********Data Info***********\n")
  79. print(" UAV range = %4d" %data.UAVrange)
  80. print(" Customer's number = %4d" %customerNum1)
  81. print(" UAV's eligible CusNum = %4d\n" %data.UAV_eligible_num)
  82. # print("*****************************Distance Matrix***************************")
  83. # for i in range(data.nodeNum):
  84. # for j in range(data.nodeNum):
  85. # print("%8.4f" %(data.disMatrix[i][j]), end = " ")
  86. # print()
  87. # print()
  88. #Reading data
  89. data = Data()
  90. uavSpeed = 2.0
  91. truckSpeed = 1.0
  92. readData(data, path, customerNum1)
  93. printData(data, customerNum1)
  94. # plt.scatter(data.cor_X[0], data.cor_Y[0], c='blue', alpha=1, marker=',', linewidths=5, label='depot')
  95. # plt.scatter(data.cor_X[1:-1], data.cor_Y[1:-1], c='magenta', alpha=1, marker='o', linewidths=5, label='customer')
  96. def solvePMS(cuss):
  97. # print("Function solvePMS() was called\n")
  98. uavMkspn = 0
  99. tempuavAssignments = []
  100. for i in range(len(cuss)):
  101. uavMkspn = uavMkspn + 2 * 0.5 * data.disMatrix[0][cuss[i].idNum]
  102. tempuavAssignments.append(cuss[i].idNum)
  103. uavAssignments = copy.deepcopy(tempuavAssignments)
  104. return [uavMkspn, uavAssignments]
  105. def tsp_len(dis, path):#path format: <4 2 1 3 8 5 7 6 10 9>
  106. # dis: N*N adjcent matrix
  107. # verctor length is N 1
  108. N = len(path)
  109. leng = 0
  110. for i in range(N-1): # 0 1 2 3 ... 9
  111. leng = leng + dis[path[i]][path[i+1]]
  112. leng = leng + dis[0][path[N-1]]
  113. leng = leng + dis[0][path[0]]
  114. return leng
  115. def tsp_new_path(oldpath):
  116. #change oldpath to its neighbor
  117. N = len(oldpath)
  118. if(random.random() < 0.25): # generate two positions and change them
  119. chpos = random.sample(range(N),2)
  120. newpath = copy.deepcopy(oldpath)
  121. if(chpos[0] == chpos[1]):
  122. newpath = tsp_new_path(oldpath)
  123. newpath[chpos[1]] = oldpath[chpos[0]]
  124. newpath[chpos[0]] = oldpath[chpos[1]]
  125. else: # generate three place and change a-b & b-c
  126. d = random.sample(range(N),3)
  127. d.sort()
  128. a = d[0]
  129. b = d[1]
  130. c = d[2]
  131. if (a != b and b!=c):
  132. newpath = copy.deepcopy(oldpath)
  133. newpath[a:(c+1)] = oldpath[b:(c+1)] + oldpath[a:b]
  134. else:
  135. newpath = tsp_new_path(oldpath)
  136. # print("Newpath:*********************")
  137. # print(newpath)
  138. return newpath
  139. def solveTSP(truckcuss):
  140. # print("Function solveTSP() was called\n")
  141. truckMkspn = 0
  142. truckRoute = []
  143. x0 = []
  144. for i in range(len(truckcuss)):
  145. x0.append(truckcuss[i].idNum)
  146. # x0.append()
  147. dist = data.disMatrix
  148. if(len(truckcuss)<3):
  149. # print('return function was called, which means the num of truck route=2')
  150. return [tsp_len(dist, x0), x0]
  151. MAX_ITER = 1000
  152. MAX_M = 20
  153. lambdaa = 0.97
  154. T0 = 100
  155. T = T0
  156. ite = 1
  157. x = x0
  158. xx = []
  159. xx.append(x)
  160. di = []
  161. di.append(tsp_len(dist, x0))
  162. n = 1
  163. while ite<MAX_ITER:
  164. m = 1
  165. while m<MAX_M:
  166. # generate new path()
  167. tempx = []
  168. tempx = tsp_new_path(x)
  169. newx = copy.deepcopy(tempx)
  170. #calculate distance
  171. oldl = tsp_len(dist, x)
  172. newl = tsp_len(dist, newx)
  173. if(oldl > newl): # if new path is more superier, choose new path as the next status
  174. x = copy.deepcopy(newx)
  175. xx.append(x)
  176. di.append(newl)
  177. n = n + 1
  178. m = m + 1
  179. ite = ite + 1
  180. T = T * lambdaa
  181. def indexofMin(arr):
  182. # print("Function indexofMin() was called\n")
  183. minindex = 0
  184. currentindex = 1
  185. while currentindex < len(arr):
  186. if arr[currentindex] < arr[minindex]:
  187. minindex = currentindex
  188. currentindex += 1
  189. return minindex
  190. truckMkspn = min(di)
  191. indexMin = indexofMin(di)
  192. # print("indexMin = %4d" %(indexMin))
  193. temptruckRoute = xx[indexMin]
  194. truckRoute = copy.deepcopy(temptruckRoute)
  195. return [truckMkspn, truckRoute]
  196. def swap(umk, tmk, ua, tr):
  197. # stand for: uavMkspn, truckMkspn, uavAssignments, truckRoute
  198. print("function SWAP was called, and uav Makespan, truck makespan, UAV customers and Truck customers' are: %.2f %.2f \n"%(umk,tmk) + str(ua) + str(tr))
  199. ms = 0 # maxSavings for return
  200. intersecCus = []
  201. for ii in range(len(tr)):
  202. if(Customers[tr[ii]-1].withinRange == 1):
  203. intersecCus.append(tr[ii])
  204. n1 = len(ua)
  205. n2 = len(intersecCus)
  206. for i in range(n1):
  207. for j in range(n2):
  208. tempuci = ua[i] # 备份无人机当前待交换顾客编号
  209. tempintersecj = intersecCus[j] # 备份卡车带交换顾客编号
  210. backupua = copy.deepcopy(ua)
  211. ua.remove(tempuci)
  212. ua.append(intersecCus[j])
  213. uaP = copy.deepcopy(ua)
  214. # resotore the origin status of UAV service
  215. ua = copy.deepcopy(backupua)
  216. backuptr = copy.deepcopy(tr)
  217. tr.append(tempuci)
  218. tr.remove(tempintersecj)
  219. trP = copy.deepcopy(tr)
  220. # restore the origin status of TRUCK service
  221. tr = copy.deepcopy(backuptr)
  222. uavCusP = []
  223. truckCusP = []
  224. for g in range(len(uaP)):
  225. uavCusP.append(Customers[uaP[g]-1])
  226. for w in range(len(trP)):
  227. truckCusP.append(Customers[trP[w]-1])
  228. # Variable initialization
  229. umkP = 0
  230. tmkP = 0
  231. uasP = []
  232. trP = []
  233. [umkP, uasP] = solvePMS(uavCusP)
  234. [tmkP, trP] = solveTSP(truckCusP)
  235. objnew = max([umkP, tmkP])
  236. objold = max([umk, tmk])
  237. if(objold - objnew > ms):
  238. ms = objold - objnew
  239. umkPrime = umkP
  240. tmkPrime = tmkP
  241. uasPrime = copy.deepcopy(uasP)
  242. trPrime = copy.deepcopy(trP)
  243. if(ms>0):
  244. umk = umkPrime
  245. tmk = tmkPrime
  246. ua = copy.deepcopy(uasPrime)
  247. tr = copy.deepcopy(trPrime)
  248. return [ms, umk, tmk, ua, tr]
  249. def PDSTSPheuristic(allcus):
  250. #Initialize
  251. uavCustomers = []
  252. truckCustomers = []
  253. for i in range(len(allcus)):
  254. if(allcus[i].withinRange == 1):
  255. uavCustomers.append(allcus[i])
  256. else:
  257. truckCustomers.append(allcus[i])
  258. uavMkspn1 = 0
  259. truckMkspn1 = 0
  260. uavAssignments1 = []
  261. truckRoute1 = []
  262. [uavMkspn1, uavAssignments1] = solvePMS(uavCustomers)
  263. [truckMkspn1, truckRoute1] = solveTSP(truckCustomers)
  264. while 1:
  265. if(uavMkspn1 > truckMkspn1):
  266. maxSavings = 0
  267. backupUAVcus = copy.deepcopy(uavAssignments1) # 备份无人机顾客的ID number
  268. countUAVCusNum = len(uavAssignments1)
  269. # iPrime =0
  270. uavMkspnPrime = 0
  271. truckMkspnPrime = 0
  272. for i in range(countUAVCusNum):
  273. uavAssignmentsP = [] # 定义无人机顾客顺序
  274. truckRouteP = [] # 定义卡车顾客服务顺序
  275. uavAssignmentsPP = [] #
  276. truckRoutePP = [] #
  277. uavCustomersP = [] # 无人机顾客
  278. truckCustomersP = [] # 卡车服务顾客
  279. # 依次添加<某个无人机访问顾客>到卡车路径中
  280. tempuavA = copy.deepcopy(uavAssignments1)
  281. uavAssignments1.remove(backupUAVcus[i])
  282. uavAssignmentsP = copy.deepcopy(uavAssignments1)
  283. temptrkA = copy.deepcopy(truckRoute1)
  284. truckRoute1.append(backupUAVcus[i])
  285. truckRouteP = copy.deepcopy(truckRoute1) #注意深度copy
  286. for g in range(len(uavAssignmentsP)):
  287. uavCustomersP.append(Customers[uavAssignments1[g]-1])
  288. for w in range(len(truckRouteP)):
  289. truckCustomersP.append(Customers[truckRoute1[w]-1])
  290. # 将调换后的顾客重新计算卡车和无人机最佳访问的最短路和顾客访问顺序
  291. [uavMkspnP, uavAssignmentsPP] = solvePMS(uavCustomersP)
  292. [truckMakespnP, truckRoutePP] = solveTSP(truckCustomersP)
  293. savings = uavMkspn1 - uavMkspnP
  294. cost = truckMakespnP - truckMkspn1
  295. if((savings - cost) > maxSavings):
  296. maxSavings = savings - cost
  297. # iPrime = i
  298. # print("i = %4d" %i, end = " ")
  299. uavMkspnPrime = uavMkspnP
  300. truckMkspnPrime = truckMakespnP
  301. uavAssignmentsPrime = copy.deepcopy(uavAssignmentsPP)
  302. truckRoutePrime = copy.deepcopy(truckRoutePP)
  303. # 做完所有计算后,恢复成计算初的状态
  304. uavAssignments1 = copy.deepcopy(tempuavA)
  305. truckRoute1 = copy.deepcopy(temptrkA)
  306. if(maxSavings > 0):
  307. print("maxSaving is : %6.2f" %maxSavings)
  308. uavAssignments1 = copy.deepcopy(uavAssignmentsPrime)
  309. truckRoute1 = copy.deepcopy(truckRoutePrime)
  310. uavMkspn1 = uavMkspnPrime
  311. truckMkspn1 = truckMkspnPrime
  312. else:
  313. [maxSavings, uavMkspn1, truckMkspn1, uavAssignments1,truckRoute1] = swap(uavMkspn1, truckMkspn1, uavAssignments1, truckRoute1)
  314. if(maxSavings == 0):
  315. break
  316. else:
  317. [maxSavings, uavMkspn1, truckMkspn1, uavAssignments1,truckRoute1] = swap(uavMkspn1, truckMkspn1, uavAssignments1, truckRoute1)
  318. if(maxSavings == 0):
  319. break
  320. print("PDSTSP heuristic (Algorithm6) was successfully called!")
  321. return [uavMkspn1, truckMkspn1, uavAssignments1, truckRoute1]
  322. if __name__ == "__main__":
  323. uavMakespn = 0
  324. truckMakespn = 0
  325. uavAssign = []
  326. truckRoute = []
  327. starttime = datetime.datetime.now()
  328. [uavMakespn, truckMakespn, uavAssign, truckRoute] = PDSTSPheuristic(Customers)
  329. endtime = datetime.datetime.now()
  330. print('\n*************** The optimal solution are AS FOLLOWS: *************\n')
  331. print('UAV makespan : %5.2f' %(uavMakespn))
  332. print('Truck makespan: %5.2f' %(truckMakespn))
  333. print('UAV Assignments : ' + str(uavAssign))
  334. print('Truck Assignments: ' + str(truckRoute))
  335. strrr="run time: %d seconds" % ((endtime - starttime).seconds)
  336. print(strrr)
  337. print('\n******* Detailed path info was shown in PLOTS windows above! *****')
  338. # draw the route graph
  339. # draw all the nodes first
  340. # data1 = Data()
  341. # readData(data1, path, 100)
  342. fig = plt.figure(figsize=(15,10))
  343. font_dict = {'family': 'Arial', # serif
  344. 'style': 'normal', # 'italic',
  345. 'weight': 'normal',
  346. 'color': 'darkred',
  347. 'size': 30,
  348. }
  349. font_dict2 = {'family': 'Arial', # serif
  350. 'style': 'normal', # 'italQic',
  351. 'weight': 'normal',
  352. 'color': 'darkred',
  353. 'size': 24,
  354. }
  355. plt.xlabel('x', font_dict)
  356. plt.ylabel('y', font_dict)
  357. plt.title('Optimal Solution for PDSTSP heuristic', font_dict)
  358. plt.xticks(fontsize=22)
  359. plt.yticks(fontsize=22) # plt.yticks(fontsize=30)
  360. plt.grid(True, color='r', linestyle='-', linewidth=2)
  361. '''
  362. marker='o'
  363. marker=','
  364. marker='.'
  365. marker=(9, 3, 30)
  366. marker='+'
  367. marker='v'
  368. marker='^'
  369. marker='<'
  370. marker='>'
  371. marker='1'
  372. marker='2'
  373. marker='3'
  374. red blue green
  375. '''
  376. plt.scatter(data.cor_X[0], data.cor_Y[0], c='blue', alpha=1, marker=',', linewidths=5, label='depot')
  377. plt.scatter(data.cor_X[1:-1], data.cor_Y[1:-1], c='magenta', alpha=1, marker='o', linewidths=5, label='customer')
  378. # Drew the route
  379. lengthTR = len(truckRoute)
  380. for i in range(lengthTR-1):
  381. x = [Customers[truckRoute[i]-1].x_cor, Customers[truckRoute[i+1]-1].x_cor]
  382. y = [Customers[truckRoute[i]-1].y_cor, Customers[truckRoute[i+1]-1].y_cor]
  383. plt.plot(x, y, 'b', linewidth = 3)
  384. plt.text(Customers[truckRoute[i]-1].x_cor-0.2, Customers[truckRoute[i]-1].y_cor, str(truckRoute[i]), fontdict = font_dict2)
  385. # conect depot to the first customer
  386. # x = [data.cor_X[0], Customers[truckRoute[0]-1].x_cor]
  387. # y = [data.cor_Y[0], Customers[truckRoute[0]-1].y_cor]
  388. x = [data.cor_X[0], data.cor_X[truckRoute[0]]]
  389. y = [data.cor_Y[0], data.cor_Y[truckRoute[0]]]
  390. plt.plot(x, y, 'b', linewidth = 3)
  391. plt.text(data.cor_X[truckRoute[0]]-0.2, data.cor_Y[truckRoute[0]], str(truckRoute[0]), fontdict = font_dict2)
  392. # conect depot to the last customer
  393. x = [data.cor_X[0], data.cor_X[truckRoute[lengthTR-1]]]
  394. y = [data.cor_Y[0], data.cor_Y[truckRoute[lengthTR-1]]]
  395. plt.plot(x, y, 'b', linewidth = 3)
  396. plt.text(data.cor_X[truckRoute[lengthTR-1]]-0.2, data.cor_Y[truckRoute[lengthTR-1]], str(truckRoute[lengthTR-1]), fontdict = font_dict2)
  397. for i in range(len(uavAssign)):
  398. x = [data.cor_X[0], data.cor_X[uavAssign[i]]]
  399. y = [data.cor_Y[0], data.cor_Y[uavAssign[i]]]
  400. plt.plot(x, y, 'r--', linewidth = 3)
  401. plt.text(data.cor_X[uavAssign[i]]-0.2, data.cor_Y[uavAssign[i]], str(uavAssign[i]), fontdict=font_dict2)
  402. #plt.grid(True)
  403. plt.grid(False)
  404. plt.legend(loc='best', fontsize = 20)
  405. plt.show()

需要指出的是,代码的实现由于原论文中只给出了数据集的生成原则,并没有数据集数据的具体信息。我们借用了solomn的数据集进行实验,刚开始主要在c101.txt上进行,txt文件的打开信息如下:

  1. RANGE
  2. 20
  3. LUNCTING RECOVER
  4. 1 1
  5. CUSTOMER
  6. CUST NO. XCOORD. YCOORD. DEMAND READY TIME DUE DATE SERVICE TIME
  7. 0 40 50 0 0 1236 0
  8. 1 45 68 10 912 967 90
  9. 2 45 70 30 825 870 90
  10. 3 42 66 10 65 146 90
  11. 4 42 68 10 727 782 90
  12. 5 42 65 10 15 67 90
  13. 6 40 69 20 621 702 90
  14. 7 40 66 20 170 225 90
  15. 8 38 68 20 255 324 90
  16. 9 38 70 10 534 605 90
  17. 10 35 66 10 357 410 90
  18. 11 35 69 10 448 505 90
  19. 12 25 85 20 652 721 90
  20. 13 22 75 30 30 92 90
  21. 14 22 85 10 567 620 90
  22. 15 20 80 40 384 429 90
  23. 16 20 85 40 475 528 90
  24. 17 18 75 20 99 148 90
  25. 18 15 75 20 179 254 90
  26. 19 15 80 10 278 345 90
  27. 20 30 50 10 10 73 90
  28. 21 30 52 20 914 965 90
  29. 22 28 52 20 812 883 90
  30. 23 28 55 10 732 777 90
  31. 24 25 50 10 65 144 90
  32. 25 25 52 40 169 224 90
  33. 26 25 55 10 622 701 90
  34. 27 23 52 10 261 316 90
  35. 28 23 55 20 546 593 90
  36. 29 20 50 10 358 405 90
  37. 30 20 55 10 449 504 90
  38. 31 10 35 20 200 237 90
  39. 32 10 40 30 31 100 90
  40. 33 8 40 40 87 158 90
  41. 34 8 45 20 751 816 90
  42. 35 5 35 10 283 344 90
  43. 36 5 45 10 665 716 90
  44. 37 2 40 20 383 434 90
  45. 38 0 40 30 479 522 90
  46. 39 0 45 20 567 624 90
  47. 40 35 30 10 264 321 90
  48. 41 35 32 10 166 235 90
  49. 42 33 32 20 68 149 90
  50. 43 33 35 10 16 80 90
  51. 44 32 30 10 359 412 90
  52. 45 30 30 10 541 600 90
  53. 46 30 32 30 448 509 90
  54. 47 30 35 10 1054 1127 90
  55. 48 28 30 10 632 693 90
  56. 49 28 35 10 1001 1066 90
  57. 50 26 32 10 815 880 90
  58. 51 25 30 10 725 786 90
  59. 52 25 35 10 912 969 90
  60. 53 44 5 20 286 347 90
  61. 54 42 10 40 186 257 90
  62. 55 42 15 10 95 158 90
  63. 56 40 5 30 385 436 90
  64. 57 40 15 40 35 87 90
  65. 58 38 5 30 471 534 90
  66. 59 38 15 10 651 740 90
  67. 60 35 5 20 562 629 90
  68. 61 50 30 10 531 610 90
  69. 62 50 35 20 262 317 90
  70. 63 50 40 50 171 218 90
  71. 64 48 30 10 632 693 90
  72. 65 48 40 10 76 129 90
  73. 66 47 35 10 826 875 90
  74. 67 47 40 10 12 77 90
  75. 68 45 30 10 734 777 90
  76. 69 45 35 10 916 969 90
  77. 70 95 30 30 387 456 90
  78. 71 95 35 20 293 360 90
  79. 72 53 30 10 450 505 90
  80. 73 92 30 10 478 551 90
  81. 74 53 35 50 353 412 90
  82. 75 45 65 20 997 1068 90
  83. 76 90 35 10 203 260 90
  84. 77 88 30 10 574 643 90
  85. 78 88 35 20 109 170 90
  86. 79 87 30 10 668 731 90
  87. 80 85 25 10 769 820 90
  88. 81 85 35 30 47 124 90
  89. 82 75 55 20 369 420 90
  90. 83 72 55 10 265 338 90
  91. 84 70 58 20 458 523 90
  92. 85 68 60 30 555 612 90
  93. 86 66 55 10 173 238 90
  94. 87 65 55 20 85 144 90
  95. 88 65 60 30 645 708 90
  96. 89 63 58 10 737 802 90
  97. 90 60 55 10 20 84 90
  98. 91 60 60 10 836 889 90
  99. 92 67 85 20 368 441 90
  100. 93 65 85 40 475 518 90
  101. 94 65 82 10 285 336 90
  102. 95 62 80 30 196 239 90
  103. 96 60 80 10 95 156 90
  104. 97 60 85 30 561 622 90
  105. 98 58 75 20 30 84 90
  106. 99 55 80 10 743 820 90
  107. 100 55 85 20 647 726 90

将结果可视化之后有:

 在算法对比中,SA启发式方法是在结果重复实验10次的场景下的最优解,可以看到在大多数情况下SA求解TSP的启发式都求得了问题的最优解,并且其求解的数据集顾客数为100时也是可以在652秒左右的时间求解完毕。

所有代码见:flying2322/UAV-algorithm-design: PDSTSP problem algorithm design at Weihui (github.com)https://github.com/flying2322/UAV-algorithm-design

solomn数据集:

solomn标准数据集,用于研究VRP问题_solomn数据集-交通文档类资源-CSDN文库https://download.csdn.net/download/weixin_43464653/54077805

参考文献
【1】Murray, C. C., & Chu, A.he G. (2015). The flying sidekick traveling salesman problem: Optimization of drone-assisted parcel delivery. Transportation Research Part C: Emerging Technologies, 54, 86-109. https://doi.org/10.1016/j.trc.2015.03.005

【2】Min, L., Cheng, W., 1999. A genetic algorithm for minimizing the makespan in the case of scheduling identical parallel machines. Artif. Intell. Eng. 13 (4), 399–403.

【3】Xu, J., Nagi, R., 2013. Identical parallel machine scheduling to minimise makespan and total weighted completion time: a column generation approach. Int. J. Prod. Res. 51 (23–24), 7091–7104.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/890147
推荐阅读
相关标签
  

闽ICP备14008679号