联合甘特图绘制
Erstellt am: 18. Januar 2025
Erstellt am: 18. Januar 2025
这是我画甘特路径图的函数: def draw(self, job, machine, machine_time): # 画图
C_finish, Lp, ro =self.caculate(job, machine, machine_time)
list_M, list_S, list_W = Lp[0], Lp[1], Lp[2]
textfigure,ax=plt.subplots() road, road_m, road_j = ro[0], ro[1], ro[2] for i in range(len(road)): x = road[i] y = road_m[i] plt.scatter(x, y, c='black') plt.plot(x, y, linestyle='--', label = f'agv{i+1}') # agv的路径 count = np.zeros((1,self.job_num),dtype=np.int64) for i in range(len(job)): # 每一道工序画一个小框 count[0, job[i]-1] += 1 plt.bar(x=list_S[i], bottom=list_M[i], height=0.5, width=list_W[i], orientation="horizontal",color='white',edgecolor='black') plt.text(list_S[i]+list_W[i]/32,list_M[i] + .05, f'{job[i]}.{count[0, job[i]-1]}', color='black',fontsize=10,weight='bold')#12是矩形框里字体的大小,可修改 plt.plot([C_finish,C_finish],[0,0],c='black',linestyle='-.',label='完工时间:%d'% (C_finish))#用虚线画出最晚完工时间 font1={'weight':'bold','size':22} # 汉字字体大小,可以修改 plt.xlabel("加工时间",font1) plt.title("甘特路径图",font1,loc='right') plt.ylabel("机床",font1) font2 = {'weight':'bold','size':10} # 汉字字体大小,可以修改 char = '' i = 0 for cha in road_j: char += (f'agv{i+1}:' + cha + '\n') # 各个agv的运行文字叙述 i += 1 plt.title(char, font2, loc='left') index_ls = [f'T{k}' for k in range(self.machine_num + 1)] # 纵坐标内容 scale_ls = [k for k in range(self.machine_num + 1)] # 纵坐标刻度 plt.yticks(scale_ls, index_ls) plt.axis([0,C_finish*1.1,0,self.machine_num+1]) plt.tick_params(labelsize = 22) # 坐标轴刻度字体大小,可以修改 labels=ax.get_xticklabels() [label.set_fontname('SimHei')for label in labels] plt.legend(prop={'family' : ['SimHei'], 'size' : 10}) # 标签字体大小,可以修改 plt.xlabel("加工时间",font1) plt.show()
这是主函数:import pandas as pd
import data
from fjsp import fj
from DLTS_GA import GA
path = 'Ex1.csv'
path2 = 'Ex2.csv'
df = pd.read_csv(path)
df1 = pd.read_csv(path2)
parm_data = data.deal(df,df1) # 调度数据
job_num = 4 # 工件数量
machine_num = 9 # 机器数量
agv_num = 3 # agv数量
generation = 10 # 迭代次数
popsize = 10 # 种群规模
Pcmax = .6 # 最大交叉概率
Pcmin = .3 # 最小交叉概率
Pmmax = .1 # 最大变异概率
Pmmin = .05 # 最小变异概率
small_tabu_iter = 20 # 小规模禁忌搜索迭代次数
large_tabu_iter = 50 # 大规模禁忌搜索迭代次数
tabu_length = 7 # 禁忌表长度
f = fj(job_num, machine_num, agv_num, parm_data)
# 柔性模块编码
parm_ga = [generation, popsize, Pcmax, Pcmin, Pmmax, Pmmin, small_tabu_iter, large_tabu_iter, tabu_length]
# 遗传算法参数
parm_fj = [f, parm_data] # 柔性模块和数据
ga = GA(parm_ga, parm_fj) # 遗传模块
job, machine, machinetime, result = ga.ga_total()
print('\njob:', job)
print('\nmachine:', machine)
print('\nmachinetime:', machinetime)
# 遗传算法结果
C_finish, _, ro = f.caculate(job, machine, machinetime)
print('\nresult:',result)
print('\n验证:',C_finish)
f.draw_change(result) # 完工时间随迭代次数的变化
f.draw(job, machine, machinetime)
# 甘特路径图
然后我打印出来的结果分别是:
job: [2, 4, 3, 4, 1, 3, 1, 4, 4, 1, 1, 2, 3, 4, 1, 3]
machine: [2, 4, 5, 4, 4, 1, 1, 2, 7, 6, 1, 9, 8, 3, 8, 8]
machinetime: [8, 4, 5, 5, 8, 15, 14, 4, 10, 5, 10, 5, 8, 11, 9, 6]
result: [[0, np.int64(71)], [1, np.int64(67)], [2, np.int64(64)], [3, np.int64(62)], [4, np.int64(61)], [5, np.int64(61)], [6, np.int64(61)], [7, np.int64(61)], [8, np.int64(61)], [9, np.int64(61)], [10 np.int64(61)]]
验证: 61
然后现在我想根据这些结果画一个资源联合调度方案,横坐标是时间/min,纵坐标是机床及AGV编号,从上之下是T1、T2....T9、AGV1、AGV2、AGV3这样。画资源联合调度方案的函数可以怎么写?
下面给出一个示例函数思路,用于在同一幅图中画出「机床 + AGV」的联合甘特图。该示例思路参考了你已有的 draw
函数,但将“机床”和“AGV”的调度信息统一放在一张图上。请根据自己的实际数据结构(例如:AGV的开始时间、结束时间、调度顺序等)进行适当修改。
f.caculate()
的返回结果)中获取每个 AGV 在什么时间段为哪个工件服务,并将这些时间段绘制在对应的 AGV 那几行上(y 值)。plt.barh()
(水平条形图) 或者和之前一样的 plt.bar(..., orientation='horizontal')
来画甘特条。下面的示例函数假设:
f.caculate(job, machine, machine_time)
或其他过程已经能够返回每道工序在对应机床的 (start_time, end_time)
,以及 AGV 的 (start_time, end_time, job_id, agv_id)
等信息。machine_intervals
和 agv_intervals
两个列表来分别存储机床和 AGV 的调度区段,你需要根据自己的逻辑把这些区段提取出来(或在 caculate
函数内生成)。请根据自己的数据结构进行修改和对接。
pythonimport matplotlib.pyplot as plt import numpy as np def draw_joint_schedule(self, job, machine, machine_time): """ 绘制机床 + AGV 联合甘特图 job : 工序对应的工件编号列表,比如 [2, 4, 3, ...] machine : 工序对应的机床编号列表,比如 [2, 4, 5, ...] (这里编号从1开始) machine_time: 工序对应的加工时长列表,比如 [8, 4, 5, ...] 需要你根据自己的代码逻辑,补充/修改获取机床与 AGV 的时间区间,并进行可视化。 """ # ----------------------------------------------------------------------- # 1. 根据已有函数/调度结果计算各资源的开工时间、完工时间 # 这里假设你已有这样一个函数或返回结构: # C_finish, (list_M, list_S, list_W), (road, road_m, road_j) = self.caculate(job, machine, machine_time) # 其中: # list_M[i] 表示第 i 道工序在哪个机床 (或 y 值), # list_S[i] 表示第 i 道工序的开始时间, # list_W[i] 表示第 i 道工序的持续时间(加工时长), # 具体含义要跟你的代码对应。 # ----------------------------------------------------------------------- C_finish, Lp, ro = self.caculate(job, machine, machine_time) list_M, list_S, list_W = Lp[0], Lp[1], Lp[2] # 分别是机床编号、开工时间、持续时间 # ----------------------------------------------------------------------- # 2. 准备联合绘图所需要的数据 # (1)机床区间 machine_intervals: [(start, end, machine_id, job_id, operation_index), ...] # (2)AGV 区间 agv_intervals: [(start, end, agv_id, job_id, transport_index), ...] # # 这里的构造只是一个示例,需要你根据自己的实际数据来做。 # ----------------------------------------------------------------------- machine_intervals = [] count_job_op = np.zeros((1, self.job_num), dtype=np.int64) # 记录每个工件是第几道工序 for i in range(len(job)): m_id = list_M[i] # 机床编号(注意是否从0开始 or 从1开始) s_t = list_S[i] # 该工序开始时间 dura = list_W[i] # 该工序持续时间 e_t = s_t + dura # 该工序结束时间 j_id = job[i] count_job_op[0, j_id - 1] += 1 # 工序计数:工件 j_id 的第几道工序 # 将机床加工区间添加进 machine_intervals # machine_id 用于在甘特图 Y 轴绘制时区分是哪台机床 # operation_index 用于在可视化时标注“工件.第几道工序” machine_intervals.append( (s_t, e_t, m_id, j_id, count_job_op[0, j_id - 1]) ) # ----------------------------------------------------------------------- # AGV 调度信息通常包含: 哪个工件在什么时间段由哪台 AGV 运输 # 假设你已经在 ro 等结构中拿到了 AGV 的行驶时间段 start/end, # 这里我们做一个简化示例:假设 ro 中包含了每个 AGV 的路径及时间信息。 # 你需要自己从 ro 或者其他变量里把 AGV 的 (start, end, agv_id, job_id) 整理出来。 # ----------------------------------------------------------------------- agv_intervals = [] # 假设 ro[3] (或者其他位置) 里存储了 AGV 调度区间信息,格式自行定义 # 这里仅演示一个形式 # for record in ro_agv: # # record = (start_time, end_time, agv_id, job_id) # agv_intervals.append(record) # 如果你暂时没有 AGV 的详细区间,可直接从已有路径 road, road_m, road_j 等中分析时间段, # 也可以用随机或伪数据做测试。 # ----------------------------------------------------------------------- # ----------------------------------------------------------------------- # 3. 正式开始绘图 # ----------------------------------------------------------------------- fig, ax = plt.subplots(figsize=(12, 6)) # 画布大小可自行调整 # 绘制机床的甘特条 for interval in machine_intervals: s_t, e_t, m_id, j_id, op_i = interval # y轴:机床编号 m_id。假设 m_id=1 ~ 9 对应 y=1 ~ 9 # 可能你的 m_id 原本是从 0 开始,需要做 +1 处理 # 这里假设 machine_id 就是 1 ~ 9 y_val = m_id # 宽度 width = e_t - s_t # 在 y=y_val 处画一条 bar,跨度是 [s_t, e_t] plt.barh(y=y_val, width=width, left=s_t, height=0.5, align='center', color='white', edgecolor='black') # 在条形上标注文字 “工件ID.工序序号” plt.text(s_t + width/10, y_val, f'J{j_id}.{op_i}', va='center', ha='left', color='black', fontsize=9) # 绘制 AGV 的甘特条 # 假设 AGV 的编号是 1~3,则它们在 y 轴上可以放在 10, 11, 12 这三行 for interval in agv_intervals: s_t, e_t, agv_id, j_id = interval y_val = self.machine_num + agv_id # 例如 machine_num=9, agv_id=1 => y=10 width = e_t - s_t plt.barh(y=y_val, width=width, left=s_t, height=0.5, align='center', color='lightgray', edgecolor='black') # 标注 “AGV#: 工件ID” plt.text(s_t + width/10, y_val, f'AGV{agv_id}-J{j_id}', va='center', ha='left', color='black', fontsize=9) # ----------------------------------------------------------------------- # 4. 绘制完工时间的虚线 # ----------------------------------------------------------------------- plt.plot([C_finish, C_finish], [0, 0], c='black', linestyle='-.', label=f'完工时间: {C_finish}') # ----------------------------------------------------------------------- # 5. 设置坐标轴信息 # ----------------------------------------------------------------------- # Y 轴刻度:1~9 表示机床 T1~T9, 10~12 表示 AGV1~AGV3 y_ticks = list(range(1, self.machine_num+1)) + \ list(range(self.machine_num+1, self.machine_num + self.agv_num + 1)) y_labels = [f"T{i}" for i in range(1, self.machine_num+1)] + \ [f"AGV{i}" for i in range(1, self.agv_num+1)] plt.yticks(y_ticks, y_labels) # X 轴范围稍微放大一点 plt.xlim(0, C_finish * 1.1) # Y 轴范围 plt.ylim(0.5, self.machine_num + self.agv_num + 0.5) # 坐标轴字体、标签等 font_title = {'weight': 'bold', 'size': 16} font_label = {'weight': 'bold', 'size': 12} plt.xlabel("时间 (min)", font_label) plt.ylabel("资源编号", font_label) plt.title("机床 + AGV 联合调度甘特图", font_title) plt.legend(loc='upper right', prop={'family': 'SimHei', 'size': 10}) plt.grid(axis='x', linestyle=':', color='gray', alpha=0.5) # 仅在 x 轴上加网格线 plt.tight_layout() plt.show()
获取 AGV 区间
agv_intervals
列表。agv_intervals.append((start_time, end_time, agv_id, job_id))
坐标刻度与标签
y=1
对应 T1,那么越大的 y 值会显示在图的下方。plt.gca().invert_yaxis()
)或在设置 yticks 时做一个反转的操作。不过一般甘特图都是 0 在最下面,数字越大越往上,这一点可以按你自己的偏好来做。其他细节
color
参数。job_id
的奇偶来区分),或者加一个 color map。caculate()
或者在遗传算法中拿到的 AGV 运输区间数据,来填充 agv_intervals
的部分。这是我的ro值:[[[0, 0, 4, np.int64(9), np.int64(13), np.int64(21), np.int64(25), np.int64(36), np.int64(40), np.int64(49), np.int64(49), np.int64(58), np.int64(61)], [0, 0, 3, np.int64(8), np.int64(12), np.int64(16), np.int64(20), np.int64(26), np.int64(30), np.int64(35), np.int64(35), np.int64(43), np.int64(49), np.int64(55), np.int64(61)], [0, 0, 3, 6, 11, 14, 20, 27, 30, 36, 36, np.int64(42), np.int64(48), np.int64(48), np.int64(51)]], [[0, 0, 9, 9, 8, 8, 3, 3, 8, 8, 8, 1, 0], [0, 0, 1, 1, 4, 4, 1, 1, 4, 4, 4, 4, 0, 8, 0], [0, 0, 1, 0, 2, 1, 7, 2, 1, 7, 7, 7, 1, 1, 0]], ['装载台运送4材料到T9,到T9运送o41到T8,到T8运送o42到T3,到T3运送o43到T8,到T8运送o44到T8,运送o34到卸载台', '装载台运送1材料到T1,到T1运送o11到T4,到T4运送o12到T1,到T1运送o13到T4,到T4运送o14到T4,运送o35到卸载台,到T8运送o35到卸载台', '装载台运送3材料到T1,回到装载台运送2材料到T2,到T1运送o31到T7,到T2运送o21到T1,到T7运送o32到T7,到T7运送o33到T1,运送o32到卸载台']]
下面给你一个示例思路,展示如何根据你给出的 ro
数据,提取并绘制「机床+AGV」的联合甘特图。示例代码中会演示:
job, machine, machine_time
等)中取得机床的甘特区间;ro
中提取 AGV 的“使用区间”,在同一张图里用水平条把 AGV 的忙碌时间段也表示出来;注意:
- 你的
ro
里,ro[0]
、ro[1]
、ro[2]
各自包含 3 个子列表(或字符串),看起来对应 3 台 AGV 的调度路线。- 通常,
ro[0][i]
表示第i
台 AGV 不同时刻的时间节点(如开始、到达、离开等),ro[1][i]
表示第i
台 AGV 在对应时刻所处的“地点”或“机床编号/装载台/卸载台”编号,ro[2][i]
则是文本描述。- 在甘特图的角度,我们更关心的是:在什么时间段内 AGV1 (或 AGV2、AGV3) 处于忙碌(运输)状态。
- 下述示例中,我将 AGV 的“占用”简单地视为“从某一时刻到下一时刻都在忙”,画一个水平条在
AGV1
那一行(或AGV2
,AGV3
)。- 具体你要如何区分“AGV 是在路上”还是“停在某台机床等待”等,需要你根据自己实际的调度逻辑去决定。这里给出的是最基础的做法。
pythonimport matplotlib.pyplot as plt import numpy as np def draw_joint_schedule(self, job, machine, machine_time, ro): """ 使用 机床调度数据 + ro(AGV路线数据),绘制联合甘特图: 横轴:时间/min 纵轴:上面是 T1, T2, ..., T9,下面是 AGV1, AGV2, AGV3. """ # ============================================================= # 1) 先从 (job, machine, machine_time) 中获取机床甘特区间 # —— 假设你已有 self.caculate(job, machine, machine_time) # 会返回 (C_finish, (list_M, list_S, list_W), ro),其中: # list_M[i]: 第 i 道工序使用的机床编号(1~machine_num) # list_S[i]: 该工序开始时间 # list_W[i]: 该工序持续时间 # ============================================================= C_finish, Lp, _ = self.caculate(job, machine, machine_time) list_M, list_S, list_W = Lp # 分别是 机床编号, 开始时间, 持续时间 # 记录机床的区间 (start, end, machine_id, job_id, operation_index) machine_intervals = [] count_job_op = np.zeros((1, self.job_num), dtype=np.int64) for i in range(len(job)): m_id = list_M[i] # 机床编号(注意是 1-based 还是 0-based) s_t = list_S[i] # 开始时间 dura = list_W[i] # 加工时长 e_t = s_t + dura j_id = job[i] count_job_op[0, j_id - 1] += 1 # 工序计数:工件 j_id 的第几道工序 machine_intervals.append( (s_t, e_t, m_id, j_id, count_job_op[0, j_id - 1]) ) # ============================================================= # 2) 再解析 ro, 得到每台 AGV 的(忙碌)区间 # ro 的结构: # ro[0] = [ [AGV1 的时间序列], [AGV2 的时间序列], [AGV3 的时间序列] ], # ro[1] = [ [AGV1 的地点序列], [AGV2 的地点序列], [AGV3 的地点序列] ], # ro[2] = [ AGV1的文字描述, AGV2的文字描述, AGV3的文字描述 ] # # 可能 ro[0][0] = [0, 0, 4, 9, 13, ... 61], # ro[1][0] = [0, 0, 9, 9, 8, ... 0] # 表示 AGV1 在这些时间点,地点发生了变化。 # # 简单做法:只要时间在 [t_k, t_{k+1}],我们就认为 AGV 忙碌(或在运输)。 # ============================================================= agv_intervals = [] # 这里假设你有 3 台 AGV for agv_id in range(self.agv_num): time_list = ro[0][agv_id] # 某台AGV的时间序列 # place_list = ro[1][agv_id] # 对应地点/机床编号,如果你需要可以用它 # 遍历 time_list,相邻两时间之间,AGV 都是“忙碌”的 for idx in range(len(time_list) - 1): start_t = time_list[idx] end_t = time_list[idx+1] # 如果两个时间相同,可能表示瞬时动作(如 0->0),可自行筛除 if end_t > start_t: agv_intervals.append((start_t, end_t, agv_id+1)) # agv_id+1 表示 AGV 编号(从 1 开始) # ============================================================= # 3) 开始在同一张图里绘制 # 纵坐标顺序希望是 T1..T9..AGV1..AGV3(上到下) # 在 matplotlib 默认坐标系中,y=1 会在下边,y=12 在上边。 # 如果想让 T1 在上,就得么“反转”y轴,或对 y 做个转换。 # 这里演示最简单的方法:让 T1=1, T2=2, ..., T9=9, AGV1=10,11,12 ... # 再通过 invert_yaxis() 让图上下颠倒。 # ============================================================= fig, ax = plt.subplots(figsize=(12, 6)) # (a) 绘制机床甘特 for interval in machine_intervals: s_t, e_t, m_id, j_id, op_i = interval y_val = m_id # 假设机床 m_id 就是 1~9 width = e_t - s_t # 在 y=y_val 画个 bar plt.barh( y=y_val, # 对应机床 y 值 width=width, # 条形宽度 = 加工时长 left=s_t, # 条形开始位置 = 开始时间 height=0.5, # 条形高度 color='white', edgecolor='black' ) # 在条形上写 工件.工序 plt.text( s_t + width/10, y_val, f'J{j_id}.{op_i}', va='center', ha='left', fontsize=8, color='black' ) # (b) 绘制 AGV 甘特 # 我们让 AGV1 在 y=10, AGV2=11, AGV3=12 # 所以 agv_id = 1 => y=10; agv_id = 2 => y=11; agv_id = 3 => y=12 for (start_t, end_t, agv_id) in agv_intervals: y_val = self.machine_num + agv_id # machine_num=9 => AGV1=10, AGV2=11, AGV3=12 width = end_t - start_t plt.barh( y=y_val, width=width, left=start_t, height=0.5, color='lightblue', # 和机床区分 edgecolor='black' ) # 标注 “AGV i” plt.text( start_t + width/10, y_val, f'AGV{agv_id}', va='center', ha='left', fontsize=8, color='black' ) # (c) 绘制完工时间的竖虚线 plt.axvline(x=C_finish, color='red', linestyle='--', label=f'C_finish={C_finish}') # ============================================================= # 4) 设置坐标轴和标题 # ============================================================= # Y 轴:1..9 => T1..T9, 10..12 => AGV1..AGV3 y_ticks = list(range(1, self.machine_num+1)) + \ list(range(self.machine_num+1, self.machine_num+self.agv_num+1)) y_labels = [f"T{i}" for i in range(1, self.machine_num+1)] + \ [f"AGV{i}" for i in range(1, self.agv_num+1)] plt.yticks(y_ticks, y_labels) # 如果想让 T1 在图的最上方,可以反转 y 轴 plt.gca().invert_yaxis() plt.xlim(0, C_finish*1.1) plt.ylim(self.machine_num+self.agv_num+0.5, 0.5) # 这里也可以自己微调 plt.xlabel("时间 (min)", fontweight='bold') plt.ylabel("机床 / AGV 资源", fontweight='bold') plt.title("机床+AGV 联合甘特图 (示例)", fontsize=14, fontweight='bold') plt.legend(loc='upper right', prop={'size': 10}) plt.grid(axis='x', linestyle=':', color='gray', alpha=0.5) plt.tight_layout() plt.show()
提取机床加工区间
self.caculate(job, machine, machine_time)
,得到 (C_finish, (list_M, list_S, list_W), ro)
;list_M[i]
是机床编号,list_S[i]
、list_W[i]
分别是工序的开始时间与持续时间;machine_intervals
,格式为 (start_time, end_time, machine_id, job_id, 第几道工序)
。提取 AGV 忙碌区间
ro
有 3 个子列表(分别对应 3 台 AGV);ro[0][0] = [0, 0, 4, 9, ...]
表示第 1 台 AGV 在若干个时间点上发生状态改变;time_list[idx]
和 time_list[idx+1]
,构造一个区间 (start_t, end_t, agv_id)
;表示这个 AGV 在 [start_t, end_t]
期间是忙碌的。ro[1][agv_id]
(地点信息) 来区分“AGV 处于运动”还是“AGV 等待”还是“空闲”,从而在甘特图上画不同的颜色或做更精细的区分。上面只是示例演示。在同一张图上绘制
T1 ~ T9
放在 y=1..9
;1 ~ 3
放在 y=10..12
;plt.barh()
画出时间跨度(甘特条),再 text
标注一些信息;plt.gca().invert_yaxis()
翻转 Y 轴。如何区分更多细节
ro[1][agv_id]
(地点序列)、ro[2][agv_id]
(文字描述)自行解析,把对应的文字标注到条形上。(start_t, end_t)
的这段标记为“AGV1: 从 T9 运送到 T8 (工件4)”。place_list[idx]
和 place_list[idx+1]
,结合 ro[2][agv_id]
的描述,拼出文本,然后在 plt.text()
里显示。job/machine/machine_time + ro
数据中分别提取机床调度和 AGV 使用区间,再在同一个甘特图里绘制出来。ro[0]
, ro[1]
的含义(时间节点、地点节点)来定义 AGV 在每段时间区间内的状态。如果相邻时间点确实表示 AGV 始终在忙碌中,那么就可以像示例那样一段一段地画。