写给那些准备入门车间调度问题的小伙伴,关于代码编写以及高效利用他人代码的方法(不要让代码能力限制了你的科研能力)

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

然后主函数,部分修改即可,然后一个可调式的完整代码就完成啦。还要修改的话,就广泛阅读论文了。

具体改编代码可见:

Aihong-Sun/Gray_wolf_optimizer_for_selectibe_assmbly: this repo proposes a optimal selective assembly Method for remanufacturing product considering quality,cost and resource utilization (github.com)https://github.com/Aihong-Sun/Gray_wolf_optimizer_for_selectibe_assmbly不要让代码能力限制了你的科研能力,哈哈哈!

其他,如强化学习等求解车间调度问题同样学习方法,祝大家科研顺利哟!

以上是关于写给那些准备入门车间调度问题的小伙伴,关于代码编写以及高效利用他人代码的方法(不要让代码能力限制了你的科研能力)的主要内容,如果未能解决你的问题,请参考以下文章

用Python实现基于遗传算法(GA)求解混合流水车间调度问题(HFSP)

李小平委员等||混合等待流水车间调度问题的迭代贪心算法

车间动态调度的研究方法

车间动态调度的研究方法

写给准备学习Linux的人

写给准备要跳槽的小伙伴们的一些建议(个人的经验与教训)