写给那些准备入门车间调度问题的小伙伴,关于代码编写以及高效利用他人代码的方法(不要让代码能力限制了你的科研能力)
Posted 码丽莲梦露
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了写给那些准备入门车间调度问题的小伙伴,关于代码编写以及高效利用他人代码的方法(不要让代码能力限制了你的科研能力)相关的知识,希望对你有一定的参考价值。
个人运作这个CSDN也有一年了,这一年中遇到很多人问我代码的问题,尤其是做车间调度的同学,大部分同学都是没有编程经验的,很多时候无从下手,不知道从什么地方开始编,今天就讲讲我的一些经验。
1 编码流程
我比较喜欢把算法和模型分开,这样的话你修改算法或修改模型就很方便,只要接口做好就好了,通常,我建议先把模型搭建好,然后再加入算法进行求解。比如你是要求解JSP问题,首先确定是单目标还是多目标、动态调度还是静态调度、考虑什么样的约束条件,解码采用那种方式,贪婪解码还是怎样,这样就把模型搭建好了,然后是算法,算法有那些模块,比如你要使用GA,那么就有编码,解码,交叉,变异,从零到一,必然应该从编码开始,之后别的模块再写起来,最后把所有的连接起来就可以了。
2 模型构建
为什么说要先将模型编好,然后在考虑算法呢,现有车间调度的研究开源还是较少的,而且考虑约束条件不同,环境便会改变,万变不离其中,只要是车间调度问题,便一定有机器、工件,或针对问题的不同,还考虑夹具、AGV、人等相关因素,我一般都喜欢将这些封装成一个类,然后解码就只需在机器或其他上加相应的时间,和工作印迹,以往我的文章也可以看出,具体模块设置已上传Github,需要可至下载:
Aihong-Sun/Shop_Floor_Item: this repo is use to share item of Shop Floor (github.com)https://github.com/Aihong-Sun/Shop_Floor_Item/blob/main/AGV.py 具体使用,调用即可,然后建立调度模型,一个考虑AGV和机器联合调度框架如下:
from *** import Job
from *** import AGV
from *** import Machine
class Shop_Floor_Env:
def __init__(self, ):
self. Create_Item()
def Create_MA(self):
self.Machines = []
for i in range(self.M_num):
M = Machine(i)
self.Machines.append(M)
self.AGVs=[]
for j in range(self.AGV_num):
A = AGV(j)
self.AGVs.append(A)
#完工时间
def C_max(self):
t=0
for J in self.Jobs:
if t<J.end:
t=J.end
return t
#运输时间
def T_trans(self):
t=0
for agv in self.AGVs:
for using_time in agv.using_time:
t=t+using_time[1]-using_time[0]
return t
def Create_Job(self):
self.Jobs = []
for k in range(self.J_num):
J = Job(k, self.PT[k])
self.Jobs.append(J)
def Create_Item(self):
self.Create_MA()
self.Create_Job()
#在此处进行模型构建,加入约束条件等,然后将时间放进相应模块的start、end进行记录
def scheduling(self,):
pass
3 算法构建
在搭建好模型后,算法就很简单了,算法的初次学习,不妨参考一下鲁迅先生的《拿来主义》,“窗外有两个树,一棵是枣树,另外一颗还是棵树”,噢不对,跑偏了,哈哈哈。应该是:“所以我们要运用脑髓,放出眼光,自己来拿!”、“我们要或使用,或存放,或毁灭。那么,主人是新主人,宅子也就会成为新宅子。”,同样,在面对这个问题,算法也是一样,目前很多算法,无论启发式算法、强化学习算法,尤其深度强化学习的研究,在别的问题上已经先行,开源代码也很健全,那么这个时候,如果你还想着自己闭门造车,从理论开始自己写的话,那可能就太慢了,而且效果未必会好,我们可以找找已有的算法实现,看看人家是怎么做的,接口怎么样,然后把自己的模型跟该算法的接口做好,第一步实现就没问题了,这就是拿来主义,但是要会拿会用才行,一个算法怎么改呢,首先你要知道对方用这个算法的问题部分是哪个部分,然后把别人 的问题抛弃,加入你的问题,你的问题怎么适用这个算法,做好接口就好了。
比如前段时间,有人找我写了一个灰狼优化算法求解一个再制造选配问题,对于这个问题,我是很陌生了,灰狼算法和再制造选配我都不熟悉,那这应该怎么做呢?
我按照模型、算法分开实现,用了一天时间就复现出来了,当然,这不是说为了吹牛说自己快,只是想告诉大家,这样做复现论文是很快的,你想,平时看论文的时候,很多问题模型大同小异,只是算法更换或修改,如果你每次复现都从头开始,有多少的时间能做,对吧。
复现的论文为:
但将原文的粒子群算法换为了灰狼算法,
首先,将问题模型搭建起来,模型主要包括的模块就有,部件,比如:主轴、齿轮,建立对应模块的编码,然后整体模型是部件的装配,目标为:装配精度和成本,建立这个模型:
from Test_data import A0,target_size,Size,Cost,delta_y,Nam_Lis
from parts import Spindle,Gear
class Assemble:
def __init__(self,A0,target_size,Size,Cost,delta_y,Nam_Lis):
self.Nam_Lis=Nam_Lis
self.delta_y=delta_y
self.A0=A0
self.target_size=target_size
self.Size=Size
self.Cost=Cost
self.Create_parts()
self.Tdelta_t=0.020 #这个根据原文要求所定,即最大间隙不应超过0.020mm.
def Create_parts(self):
self.Spindles=[] #里面装CAK6150主轴类
self.Gear1s=[] #里面装02065L1齿轮类
self.Gear2s=[] #里面装02405L齿轮类
for i in range(len(self.A0)):
if i==0: #第一位为主轴
for j in range(len(self.Cost[i])):
Sp=Spindle(self.Nam_Lis[i][j],self.A0[i],self.target_size[i][0],self.target_size[i][1],
self.Size[i][0][j],self.Size[i][1][i],self.Cost[i][j],self.delta_y[i][0],self.delta_y[i][1])
self.Spindles.append(Sp)
elif i==1:
for k in range(len(self.Cost[i])):
g1=Gear(self.Nam_Lis[i][k],self.A0[i],self.target_size[i][0],self.Size[i][0][k],self.Cost[i][k],self.delta_y[i][0])
self.Gear1s.append(g1)
else:
for k in range(len(self.Cost[i])):
g2=Gear(self.Nam_Lis[i][k],self.A0[i],self.target_size[i][0],self.Size[i][0][k],self.Cost[i][k],self.delta_y[i][0])
self.Gear2s.append(g2)
#封闭环尺寸链约束函数
def Dimension_chain(self,S,G1,G2):
'''
:param S: 为一个主轴类
:param G1: 为齿轮1类
:param G2: 为齿轮2类
:return delta_t: 封闭尺寸偏差
'''
t1=S.D1-G1.D
t2=S.D2-G2.D
T1=bool(t1<=self.Tdelta_t)
T2=bool(t2<=self.Tdelta_t)
if T1 and T2:
return True #如何满足装配尺寸链要求,返回True
else:
return False
#质量损失成本函数
def Quality_loss_fun(self,Lis):
'''
:param Lis:
:return Loss:
'''
Loss=0
for i in range(len(Lis)):
for j in range(len(Lis[i])):
if j==0: #当部件为主轴时
k1=Lis[i][j].A0/Lis[i][j].y1
k2=Lis[i][j].A0/Lis[i][j].y1
L_y1=k1*pow((Lis[i][j].D1-Lis[i][j].target_D1),2) #
L_y2 = k2 * pow((Lis[i][j].D2 - Lis[i][j].target_D2),2) #
Loss=Loss+L_y1+L_y2
else:
k=Lis[i][j].A0/Lis[i][j].y
L_y = k * (Lis[i][j].D - Lis[i][j].target_D)
Loss+=L_y
return Loss
#剩余件成本函数
def Cost_of_surplus_parts(self,Lis):
'''
:param Lis:
:return:
'''
Ji=0 #参与装配的再制造件i的加工成本
for Li in Lis:
Ji+=Li.Processing_cost
G=0 #未参与装配的再制造件及再利用件的剩余再制造零件的总成本
for Si in self.Spindles:
if Si not in Lis and 'N'not in Si.Nam: #判断主轴是再利用或再制造件且没有被利用的件
G+=Si.r
for Sj in self.Gear1s:
if Sj not in Lis and 'N' not in Sj.Nam: # 判断齿轮1是再利用或再制造件且没有被利用的件
G += Sj.r
for Sk in self.Gear2s:
if Sk not in Lis and 'N' not in Sk.Nam: # 判断齿轮1是再利用或再制造件且没有被利用的件
G += Sk.r
C=Ji+G
return C
def show(self,Lis):
for i in range(len(Lis)):
print('第',i+1,'个零件组合')
for j in range(len(Lis[i])):
if j==0:
print(self.Spindles[Lis[i][j]].Nam)
elif j==1:
print(self.Gear1s[Lis[i][j]].Nam)
else:
print(self.Gear2s[Lis[i][j]].Nam)
Ass=Assemble(A0,target_size,Size,Cost,delta_y,Nam_Lis)
然后就是拿来主义——算法,哈哈哈,首先了解一下这个算法的基本原理:
【优化算法】简述灰狼优化算法(GWO)原理_蓝色蛋黄包的博客-CSDN博客_灰狼算法https://blog.csdn.net/haha0332/article/details/88805910然后再Github上找一个合适的算法:
matikuto/Python-implementation-of-Gray-Wolf-Optimizer-and-Improved-Gray-Wolf-Optimizer: Implementation of GWO and i-GWO with Python 3.9 (github.com)https://github.com/matikuto/Python-implementation-of-Gray-Wolf-Optimizer-and-Improved-Gray-Wolf-Optimizer修改编码方式(原编码方式为一层,此处修改为三层),即在wolf函数处修改自己编解码方式:
import random
from Assembly import Ass
from Test_data import Num_part
class wolf:
def __init__(self,Lis,minx,maxx,position=None):
'''
:param Lis: 传入的是各零件的个数,例如本文的实验中主轴有6个,齿轮1、齿轮2分别有10个,则Lis=[6,10,10]
minx,maxx:分别表示每个元素的取值范围
'''
self.Lis=Lis
if position==None:
self.Position = []
for i in Lis: #这里参考我写的程序说明中的编码方式
Pi=[]
for j in range(i):
Pi.append(((maxx - minx) * random.random() + minx))
self.Position.append(Pi)
else:
self.Position=position
self.fit()
def Decode(self):
#按照程序说明中的解码部分进行理解
Pos_change=[]
for Pi in self.Position:
Posi=dict(enumerate(Pi))
Posi = sorted(Posi.items(), key=lambda x: x[1], reverse=True)
Pos_change.append(Posi)
min_l=min(self.Lis) #找出最小的零件个数,即最多可装配min_l个装配体
selective_scheme=[] #选配方案
for i in range(min_l):
Parts=[]
for j in range(len(Pos_change)):
pc=Pos_change[j][i][0]
Parts.append(pc)
selective_scheme.append(Parts)
return selective_scheme
def fit(self):
selective_scheme=self.Decode()
#判断组合是否满足封闭尺寸链
Meet_quality=[] #存入满足封闭尺寸链的零件组合
Use_parts=[] #参与使用的零件
for Si in selective_scheme:
Part_Lis=[] #一组零件组合
for i in range(len(Si)):
if i==0:
Part_Lis.append(Ass.Spindles[Si[i]]) #主轴
elif i==1:
Part_Lis.append(Ass.Gear1s[Si[i]]) #齿轮1
else:
Part_Lis.append(Ass.Gear2s[Si[i]]) #齿轮2
if Ass.Dimension_chain(Part_Lis[0],Part_Lis[1],Part_Lis[2]): #判断是否满足封闭尺寸链
Meet_quality.append(Part_Lis)
Use_parts.extend(Part_Lis)
Loss=Ass.Quality_loss_fun(Meet_quality)
C=Ass.Cost_of_surplus_parts(Use_parts)
self.fitness=Loss+C
w=wolf(Num_part,-2,2) #在这里,minx,maxx取-2,2
然后主函数,部分修改即可,然后一个可调式的完整代码就完成啦。还要修改的话,就广泛阅读论文了。
具体改编代码可见:
其他,如强化学习等求解车间调度问题同样学习方法,祝大家科研顺利哟!
以上是关于写给那些准备入门车间调度问题的小伙伴,关于代码编写以及高效利用他人代码的方法(不要让代码能力限制了你的科研能力)的主要内容,如果未能解决你的问题,请参考以下文章