柔性车间调度丨模拟退火算法研究:以算例MK01为例

车间调度系列文章:

1、柔性车间调度问题

柔性车间调度问题可描述为:多个工件在多台机器上加工,工件安排加工时严格按照工序的先后顺序,至少有一道工序有多个可加工机器,在某些优化目标下安排生产。柔性车间调度问题的约束条件如下:

  • (1)同一台机器同一时刻只能加工一个工件;
  • (2)同一工件的同一道工序在同一时刻被加工的机器数是一;
  • (3)任意工序开始加工不能中断;
  • (4)各个工件之间不存在的优先级的差别;
  • (5)同一工件的工序之间存在先后约束,不同工件的工序之间不存在先后约束;
  • (6)所有工件在零时刻都可以被加工。

MK01算例:
在这里插入图片描述
算例的工件数和机器数分别是10和6。

excel的第一行和第一列是编号,不用考虑,修改与否也不影响。

第一行第一个数字6表示,工件1有6道工序。后面的2 1 5 3 4,表示工件1的第一道工序有两个可选机器,分别是1和3,加工时间是5和4,后面的3 5 3 3 5 2 1表示工件1的第二道工序有3个可选机器,分别是5,3,2,加工时间是3,5,1,一行就是1个工件的所有工序的可选机器可加工时间,后面的工序以此类推。

2、数学模型

符号定义:

n工件总数makespani工件i的完工时间
m机器总数makespan最大完工时间
i,h工件号Load_all机器总负荷
j,k工序号E_all总能耗
z机器号Xijz工序oij是否在机器z上加工,为0-1变量,在z上加工为1
qi工件i的工序数Gijhk工序oij和工序ohk的先后顺序,为0-1变量,ij在前为1
oij工件i的第j道工序M一个大正实数
Mij工序oij的可选机器Tijz工序oij在机器z的加工时间
Sij工序oij的开工时间Fz机器的z的完工时间
Cij工序oij的完工时间Pz机器的z的负载功率
Load_z机器z负荷Rz机器的z的空载功率

模型:

目标函数:

makespan=min{max makespani} 完工时间

i=(1,2,…,n)

约束条件:

3、柔性作业车间工具

工序编码

  • 步骤1:按照工件的工序数依次生成工序编码如下:

work = [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9]

程序里为方便运算,0表示工件1,依次类推。

  • 步骤2:随机打乱work得到如下编码:

job= [7, 1, 7, 9, 4, 6, 4, 2, 4, 6, 5, 0, 5, 1, 1, 5, 1, 6, 3, 2, 4, 9, 2, 3, 8, 8, 4, 0, 0, 7, 7, 6, 1, 8, 9, 0, 2, 9, 3, 6, 8, 7, 5, 8, 9, 9, 3, 4, 3, 2, 5, 5, 0, 0, 8]

job就是一个可行工序编码。

机器和加工时间编码:

参考文献的3种机器编码生成方法:全局选择、局部选择和随机选择。

对于6个加工机器的mk01。

全局选择:依次安排每个工件的加工,每道工序选择最小负荷的机器。

局部选择:依次安排每个工件的加工,每次安排完一个工件加工后,各个机器的负荷清0,每道工序选择最小负荷的机器。

随机选择:依次安排每个工件的加工,每道工序随机选择可加工机器

核心代码

    
                if r<self.p1 or r>1-self.p2: 
                    for k in range(len(n_machine)):
                        m=int(n_machine[k])-1
                        index_select.append(m)
                        t=n_time[k]
                        a_global[0,m]+=t               #全局负荷计算
                        a_part[0,m]+=t                 #局部负荷计算
                    
                    if r<self.p1:                       #全局选择
                        select=a_global[:,index_select]
                        idx_select=np.argmin(select[0])
                    else:                               #局部选择
                        select=a_part[:,index_select]
                        idx_select=np.argmin(select[0])
                    m_select=n_machine[idx_select]
                    t_index=n_machine.index(m_select)
                    machine.append(m_select)
                    machine_time.append(n_time[t_index])
                else:                                       #否则随机挑选机器                                
                    index=np.random.randint(0,len(n_time),1)
                    machine.append(n_machine[index[0]])
                    machine_time.append(n_time[index[0]])

一次随机生成的机器和加工时间编码如下:

machine=[3.0, 2.0, 6.0, 1.0, 3.0, 4.0, 2.0, 3.0, 1.0, 4.0, 1.0, 2.0, 6.0, 1.0, 3.0, 1.0, 1.0, 2.0, 3.0, 5.0, 6.0, 2.0, 1.0, 2.0, 1.0, 4.0, 6.0, 6.0, 1.0, 2.0, 2.0, 1.0, 4.0, 6.0, 4.0, 3.0, 5.0, 3.0, 6.0, 2.0, 1.0, 2.0, 4.0, 6.0, 1.0, 4.0, 1.0, 2.0, 4.0, 6.0, 2.0, 5.0, 6.0, 4.0, 1.0]

machine_time=[4.0, 1.0, 2.0, 1.0, 1.0, 3.0, 6.0, 1.0, 2.0, 6.0, 1.0, 6.0, 2.0, 1.0, 4.0, 1.0, 1.0, 6.0, 1.0, 3.0, 2.0, 1.0, 1.0, 6.0, 5.0, 6.0, 6.0, 2.0, 2.0, 6.0, 6.0, 1.0, 2.0, 1.0, 2.0, 4.0, 1.0, 1.0, 2.0, 6.0, 1.0, 6.0, 6.0, 1.0, 1.0, 3.0, 2.0, 6.0, 6.0, 2.0, 6.0, 3.0, 1.0, 6.0, 3.0]

由算例知道工件1有6道工序,所以machine和machine_time的前6个数表示工件1的6道工序依次在机器3.0、2.0、6.0、 1.0、 3.0、4.0加工,加工时间是4.0、1.0、2.0、 1.0、1.0、 3.0。小数点是因为数据类型是浮点型,不影响,为了美观也可变为整型。

同理工件2有5道工序,所以machine和machine_time的第7到11个数表示工件2的5道工序依次在机器 2.0、3.0、1.0、4.0、 1.0加工,加工时间是 6.0, 1.0, 2.0, 6.0, 1.0,后续编码依次类推。

插入式解码:

简单来说:每次安排工序的加工机器,首先找对应加工机器的空闲时间,尽量把工序安排空闲时间里。
核心代码:

            if jobtime[0,svg] >0 :                            #如果工序不是第一道工序
                if len(rest[sig])>0 :                         #如果空闲时间非空
                    for m in range(len(rest[sig])-1,-1,-1):   #空闲时间从后往前遍历
                        if rest[sig][m][1]<=jobtime[0,svg] :  #如果某个空闲时间段小于等于上一道工序的完工时间
                            break                             #结束遍历
                        else:                                 #否则
                            begin=max(jobtime[0,svg],rest[sig][m][0])  #可开工时间是上一道工序的完工时间和空闲片段开始时间最大值
                            
                            if begin+machine_time[index] <= rest[sig][m][1] : #如果空闲时间段满足要求
                                startime=begin                #更新开工时间
                                signal=1
                                del rest[sig][m]              #删掉空闲时间段
                                break
            
            if signal==0 :                                    #如果不可插入
                startime=max(jobtime[0,svg],tmm[0,sig])       #开工时间是加工机器结束时间和上一道工序完工时间的最大值

            if startime>tmm[0,sig] and signal==0:             #如果不可插入且开工时间大于加工机器的完工时间
                rest[sig].append([tmm[0,sig],startime])       #添加时间段到空闲时间里
            if signal==0 :                                    #如果不可插入
                tmm[0,sig]=startime+machine_time[index]       #更新机器的结束时间
            if signal>0 :                                     #如果可插入
                signal=0                                      #不更新机器结束时间,且可插入信号归零

            jobtime[0,svg]=startime+machine_time[index]       #更新工序完工时间

对本文来说,一次插入成功后,就把当前插入的空闲时间段删除,虽然插入后,时间段仍然可能剩余空闲时间,但认为剩余较短,不考虑。

代码在fjsp.py里。

4、 模拟退火算法

算法介绍:

模拟退火算法(Simulated Annealing,SA)最早的思想是由N. Metropolis 等人于1953年提出。1983 年,S. Kirkpatrick 等成功地将退火思想引入到组合优化领域。它是基于Monte-Carlo迭代求解策略的一种随机寻优算法,其出发点是基于物理中固体物质的退火过程与一般组合优化问题之间的相似性。 从某一较高初温出发,伴随温度参数的不断下降,结合概率突跳特性在求解空间中随机寻找目标函数的全局最优解,即在局部最优解能概率性地跳出并最终趋于全局最优。

算法步骤

1、初始温度T0 , 令 T = T0,随机产生一个初始解 X0 ,并计算对应的目标函数值 E(x0) ,本文是makespan。
2、令T=rT, 其中r取值0到1之间,为温度下降速。
3、对当前解施加随机扰动,在其邻域内产生一个新解 ,并计算对应的完工时间,
4、按照 Metropolis准则判断是否接受新解 , 若新解的完工时间小于原解,接受新解作为当前解,否则按照概率判断是否接受。
5、在温度 T 下,重复 L 次扰动和接受过程,即执行步骤 3 和 4 ,本问的L就是种群数量。
6、判断温度是否达到终止温度水平,若是则终止算法,否则返回步骤 2
参考博客:https://blog.csdn.net/qq_44865735/article/details/124100778:

核心代码:

                if i != best_idx:          # 保留种群最优个体
                    job1, machine1, machinetime1 = w_new[i], m_new[i], t_new[i]
                    C_max, _, _, _, _ = fjsp.caculate(job1, machine1, machinetime1, self.parm_fj, self.parm_mk)
                    if C_max< answer[i]:   # 如果新的完工时间低于原解
                        answer[i] =C_max   # 接受新解
                        w[i] = job1
                        m[i] = machine1
                        t[i] = machinetime1
                    else:                 # 否则以一定概率接受原解
                        delta_e = C_max- answer[i]
                        if random.uniform(0, 1) < math.exp(-delta_e / self.T):
                            answer[i] = C_max
                            w[i] = job1
                            m[i] = machine1
                            t[i] = machinetime1

            self.T *= self.r      # 降温

本文为保留每次迭代的最优个体,最优个体不与新解比较

工序邻域搜索(工序新解产生)

随机产生2个位置,2个位置之间的编码逆序。

代码:

    def job_new(self,w):
        w_new = []
        index = random.sample(range(len(w[0])), 2)  # 随机生成两个位置
        idx = list(set(index))                      # 位置按先后顺序调整
        for wi in w:
            job1 = wi.copy()
            b = wi[idx[0]:idx[1] + 1] 
            b.reverse()                             # 逆序
            job1[idx[0]:idx[1] + 1] = b
            w_new.append(job1)
        return w_new

加工机器邻域搜索(机器新解产生)

随机产生2个位置,交换相邻2个机器编码2个位置之间的基因

代码:

 def mt_new(self, m, t):
        index = random.sample(range(len(m[0])), 2)
        idx = list(set(index))
        m_new, t_new = [], []
        for i in range(0, len(m), 2):
            m1, m2, t1, t2 = m[i].copy(), m[i + 1].copy(), t[i].copy(), t[i + 1].copy()
            a, b, c, d = m1[idx[0]:idx[1] + 1], m2[idx[0]:idx[1] + 1], t1[idx[0]:idx[1] + 1], t2[idx[0]:idx[1] + 1]
            m1[idx[0]:idx[1] + 1], m2[idx[0]:idx[1] + 1] = b, a  # 两段加工机器编码交换
            m_new.append(m1)
            m_new.append(m2)

            t1[idx[0]:idx[1] + 1], t2[idx[0]:idx[1] + 1] = d, c  # 两段加工时间编码交换
            t_new.append(t1)
            t_new.append(t2)
        return m_new, t_new

5、结果

代码运行环境
windows系统,python3.11.1,第三方库及版本号如下:

numpy==1.24.3
matplotlib==3.7.1

第三方库需要在安装完python之后,额外安装,以前文章有讲述过安装第三方库的解决办法。

命令

在main.py里

import data
from SA import simul
import fjsp

Tmachine,Tmachinetime,machines,work,start,lenth=data.total(10)
parm_fj=[10,6,0.3,0.4]                  # 编码解码参数,依次是工件数、机器数 ,全局、局部选择比例
parm_mk=[Tmachine,Tmachinetime,machines,work,start,lenth]
parm_sa = [100, 0.95, .01, 40]          # 初始温度,降温速率,终止温度,种群规模

sa = simul(parm_sa, parm_fj, parm_mk)   # 模拟退火模块
job, machine, machinetime, result = sa.simulate()      # 模拟退火算法结果

fjsp.draw(job, machine, machinetime,parm_fj,parm_mk)   # 甘特图
fjsp.change(result)                                    # 完工时间变化图

一次运行结果

部分结果:

第162次迭代,完工时间:46.0
第163次迭代,完工时间:46.0
第164次迭代,完工时间:46.0
第165次迭代,完工时间:46.0
第166次迭代,完工时间:46.0

甘特图:

在这里插入图片描述

最大完工时间随迭代次数的变化:

在这里插入图片描述

6、代码

篇幅问题,代码附在后面。

演示视频:

柔性车间调度丨模拟退火算法研究:以MK01算例为例

完整算法+数据:

# 微信公众号学长带你飞
# 主要更新方向:1、车间调度问题求解算法
#              2、学术写作技巧
#              3、读书感悟
# @Author  : Jack Hao
可加微信:diligent-1618,请备注:学校-专业-昵称。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 柔性作业车间调度问题是指在具有某些约束条件的车间中,如何合理地安排设备的使用,使得所有作业能够在规定的时间内完成,同时尽量减少生产成本。 MK算例是一种常用于求解此类调度问题的解法。其基本思路是将车间中的作业划分为若干个阶段,并按照优先级进行排序。然后,按照一定的规则分配设备,并利用各种约束条件进行求解,可以得到最优的调度方案。 对于柔性作业车间调度问题,需要考虑的约束条件包括设备工作时间,设备切换时间,作业加工时间和作业之间的交错关系等。在进行算法求解时,需要根据这些约束条件进行有效地编码和解码,以实现快速而有效的求解。 总之,柔性作业车间调度问题是一项复杂而重要的生产调度任务,MK算例是一种常用的求解算法。在实践中,需要综合考虑各种约束条件,灵活运用调度策略和技巧,以提高生产效率和降低生产成本。 ### 回答2: 柔性作业车间调度是现代工业生产中非常重要的一环,它可以有效地提高生产效率、降低成本,并且能够适应生产需求的变化,实现生产的最优化。MK算例柔性作业车间调度中一个常用的算法。 MK算例是一种基于遗传算法的作业车间调度算法。它主要分为两个步骤:首先通过遗传算法的方式对作业序列进行优化,然后再根据优化后的作业序列进行车间调度。在优化作业序列时,MK算例主要考虑每个作业任务的加工时间、加工设备、工件存储区域等因素,通过遗传算法的方式对作业任务进行排列组合,得到最佳的作业序列。在车间调度时,MK算例通过生成作业开始时间的方法,确定每个作业的开工时间、加工时间和完成时间等。同时,MK算例还可以进行异常情况的处理,如设备故障、工艺异常等情况的处理。 MK算例通过遗传算法的方式对车间调度进行优化,可以有效地降低生产成本、提高生产效率,同时还可以适应不同生产环境的需求。在实际应用中,MK算例已经被广泛地应用于工业生产的各个方面,并且在大量的案例中得到了验证和应用。 ### 回答3: 柔性作业车间调度是指在生产过程中,根据客户需求和生产情况,灵活调整车间生产计划,以达到最佳的生产效益和客户满意度。而mk算例则是一个常用的调度算法,也被广泛应用于生产制造中。 在柔性作业车间调度mk算例中,需要考虑以下几个方面: 首先是任务的分配和调度车间内有多种不同类型的设备,每个设备可以完成不同的任务。在任务分配中需要考虑到设备的类型特性,同时将任务按照其完成时间、优先级等因素进行评估,然后进行优先级调度和任务分配。 其次是设备调度车间内的设备都是有限的资源,需要对其进行合理的调度。在设备调度中需要考虑它们的特性,如加工能力、维修需要等。通过对设备负载的监控和分析,实现对设备的动态调度。 再次是车间作业调度。在车间作业调度中需要考虑的是车间内多个任务之间的相互影响。通过调整车间生产计划来防止任务之间的冲突,实现车间内各项任务的协调与统一。 最后是数据的收集和分析。在柔性作业车间调度mk算例中,需要对生产过程中的各类数据进行收集和分析,以帮助生产管理人员做出更加准确的决策。通过对数据的分析,可以及时发现生产问题,并进行及时的处理。同时也为车间内生产计划的优化提供了有力的支持。 综上所述,柔性作业车间调度mk算例是一种基于计算机算法的生产管理方式,它能够有效地帮助企业提高生产效益和降低生产成本。同时也能够提高车间生产计划的灵活性和符合度,为企业的高效发展提供有力支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值