论文复现详解丨改进蚁群求解柔性车间调度问题:以mk01为例

车间调度首图
车间调度系列文章:

一、柔性车间调度问题

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

  • (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个工件的所有工序的可选机器可加工时间,后面的工序以此类推。

二、数学模型

符号定义:

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的空载功率

模型:

目标函数:

c_max = min makespan

约束条件:

三、蚁群算法过程详解

1、初始工序编码

参考文献的30只蚂蚁,均匀放在10个工件的第一道工序上,后续随机选择。

  • 步骤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]

如第一道工序是7,job就是一个可行工序编码。

核心编码:

def job_init(self):                       # 初始30只蚂蚁均匀分布,10个工件,每个工件3只
	count = int(self.ant_num/self.job_num)
	count1 = 0
	JOB = []
	for i in range(self.ant_num):
		job1 = self.work.copy()
		job1.remove(count1)              # 取出首工序
		np.random.shuffle(job1)          # 打乱其余工序
		
		job2 = [count1] + job1           # 连接
		JOB.append(job2)

		if (i+1)%count == 0:             # 每个工件做3次,切换下一个
			count1 += 1
	return JOB

2、后续工序编码

按照论文,后续编码以0.7的概率轮盘赌选择,0.3的概率随机选择:

在这里插入图片描述

轮盘赌选择:

轮盘赌法是模拟博彩游戏的轮盘赌,扇形的面积对应它所表示的染色体的适应值的大小,适应度值被选择的可能性也就越大。轮盘赌法的关键部分是概率和累计概率的计算,具体的步骤如下:

步骤一:计算出群体中每个个体的适应度f(i=1,2,…,M),M为群体大小;计算出每个个体被遗传到下一代群体中的概率

步骤二:累加计算累积概率,如步骤1有概率为[0.2,0.3,0.5],累积概率是[0.2,0.5,1]

步骤三:随机在0,1之间产生一个数r,如果若r<q[1],则选择个体1,否则,选择个体k,使得:q[k-1]<rq[k]成立。

代码:

def rand_choose(self, p):                      #轮盘赌选择
	x = np.random.rand()
	q = 0
	for i, px in enumerate(p):
		q += px                                # 累计概率
		if x <= q:
			break
	return i

3、转移概率计算

按照参考文献,转移概率由信息素浓度和启发因子计算,启发因子比较容易,工序时间分之一,信息素浓度和是否是精英解和具体的完工时间相关:

图\3)

图4)

信息素更新核心代码:

def update_Tau(self, ant_group, answer):                                        # 更新信息素
	delta_tau = np.zeros([self.job_num, self.width])
	best_ant = min(answer)                                                     # 精英蚂蚁
	best_ant_index = [i for i in range(len(answer)) if answer[i] == best_ant] #索引精英蚂蚁索引
	best_ant_num = len(best_ant_index)                                        # 精英蚂蚁数量

	tau_best = 0                                        
	for i in range(self.width):
		for j in range(ant_group.shape[0]):
			idx = int(ant_group[j][i])
			if j in best_ant_index:
				tau_best = (best_ant_num*self.Q)/answer[j]                     # 精英信息素计算
			
			tau = self.Q/answer[j]  				                           # 一般信息素计算
			delta_tau[idx][i] = (1 -self.ρ)*delta_tau[idx][i] + tau +tau_best # 信息素更新
			
			if delta_tau[idx][i] > self.τmax:                                # 信息素约束
				delta_tau[idx][i] = self.τmax

			if delta_tau[idx][i] < self.τmin:
				delta_tau[idx][i] = self.τmin
	return delta_tau

概率矩阵计算核心代码:

def trans_p_caculate(self, delta_tau, ant_group_job, ant_group_time):
	delta_p = np.zeros([self.job_num, self.width])
	for i in range(self.width):
		τij = delta_tau[:,i]               # 信息素浓度
		ant_time = [ant_group_time[j,i] for j in range(ant_group_time.shape[0])]
		ant_job = [ant_group_job[j,i] for j in range(ant_group_time.shape[0])]
		 
		ηij = [0 for w in range(self.job_num)]
		count = 0
		for jo in ant_job:
			ηij[int(jo)] += 1/ant_time[count]
			count += 1

		ηij = np.array(ηij)                    # 启发函数

		pall = (τij**self.α) * (ηij**self.β)   # 转移概率计算
		p = pall/sum(pall)
		delta_p[:,i] = p
	return delta_p

4、机器和加工时间编码:

参考文献的机器编码生成方法:0.7选择最短加工时间,0.2选择开工时间最早,0.1随机选择。

核心代码

def machine_select_with_decode(self, job, decode_inf):
	count = np.zeros((1, self.job_num), dtype=np.int32)
	jobtime=np.zeros((1,self.job_num))        
	tmm=np.zeros((1,self.machine_num))                     
	startime=0
	list_M,list_S,list_W=[],[],[]

	r = np.random.rand()
	
	machine, machine_time = [], []  # 初始化矩阵
	for i in range(len(job)):
		svg = int(job[i])
		if len(decode_inf) == 0:         # 空矩阵做初始机器编码的生成,否则只做解码
			index1 = count[0, svg]
			highs = self.start[svg][index1]
			lows = self.start[svg][index1] - self.end[svg][index1]
			n_machine = self.Tmachine[svg, lows:highs].tolist()
			n_time = self.Tmachinetime[svg, lows:highs].tolist()

			if r < self.p1 :              # 0.7选择加工时间最短的机器
				idx = n_time.index(min(n_time))
				machine.append(n_machine[idx])
				machine_time.append(n_time[idx])
			elif r<= self.p1 + self.p2:  # .2选择最早开始加工工件的机器
				time = []
				for ma in n_machine:
					time.append(tmm[0,int(ma)-1])
				idx = time.index(min(time))
				machine.append(n_machine[idx])
				machine_time.append(n_time[idx])
				
			else:  # 否则随机挑选机器
				index = np.random.randint(0, len(n_time), 1)
				machine.append(n_machine[index[0]])
				machine_time.append(n_time[index[0]])
		if len(decode_inf) > 0:
			machine = decode_inf[0]
			machine_time = decode_inf[1]		
		sig = int(machine[i]) - 1						  
		startime=max(jobtime[0,svg],tmm[0,sig])  
		
		tmm[0,sig]=startime+machine_time[i]
		jobtime[0,svg]=startime+machine_time[i]

		list_M.append(machine[i])
		list_S.append(startime)
		list_W.append(machine_time[i])
		count[0, svg] += 1
	 

	m_last = np.argmax(tmm[0]) + 1  # 结束最晚的机器
	c_max = max(tmm[0])  # 最晚完工时间
	
	return c_max,machine, machine_time, list_M, list_S, list_W, m_last

5、解码:

安排每一道工序生产时,开工时间是上一道工序,和选择的加工机器完工时间的最大值,上面的机器编码生成时也完成解码,单独解码时由decode_inf控制。

代码主要放在fjsp.py和ACO.py里。

四、改进蚁群算法

具体的算法参考文献有介绍,简单介绍一下步骤:
算法步骤:

  • 步骤1:随机和几种机器选择方式初始多个工序、机器、加工时间编码,并解码

  • 步骤2:按照文献的方式更新信息素浓度,计算转移概率

  • 步骤3:轮盘赌和随机方式选择工序,机器选择由最小加工时间,开工时间和随机选择相结合

  • 步骤4:判断是否达到最大迭代次数,是的话输出结果,否则转到步骤2

五、结果

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

numpy==1.18.5
matplotlib==3.2.1

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

主函数

import numpy as np
from data import data_deal
from fjsp import fj
from ACO import aco


da = data_deal(10,6)
Tmachine,Tmachinetime,end,work,start,machines = da.cacu()
#print(Tmachine,Tmachinetime,tdx,work,tom,machines)

parm_fj = [10, 6, .7, .2]      # 工件数,机器数,0.7的最小机器选择,0.2的最开始加工机器选择,1-0.7-0.2的随机选择
parm_mk = [Tmachine, Tmachinetime, work,start, end]
ant_num = 30 #蚂蚁数量

f = fj(parm_fj, parm_mk, ant_num)


m, Q, α, β, ρ, τmax, τmin, Nmax = 30, 100, 1, 2, .3, 50, 1, 50
# 依次是蚂蚁数量,信息素常量,信息素因子,启发函数因子,信息素挥发因子,信息素最大值, 信息素最小值, 最大迭代次数

parm_j = [10, work, .7, f]           # 两个数字分别是工件数, 0.7的概率轮盘赌选择
ant = aco(m, Q, α, β, ρ, τmax, τmin, Nmax, parm_j)


job, machine, machine_time, result = ant.total()
c_max,machine, machine_time, list_M, list_S, list_W, m_last = f.machine_select_with_decode(job,[machine, machine_time])
print('验证:', c_max)
f.draw(job, machine, machine_time)

运行结果

一次迭代结果如下:

tu 5)

一个解的甘特图如下:

tu 6)

结论

  • 1、本文的涉及的内容较多,有编码、解码、改进算法等等。论文主要是对文献的算法进行复习,虽然对于mk01来说,结果不是太好,可能是数据问题,或者算法迭代之类的问题,可以考虑和其他算法结合,或者调整参数之类的尝试,但这是是对蚁群算法求解车间调度等离散问题的一次尝试,参考意义较大。

  • 2、excel数据可更改,工件数、机器数、工件的工序数、工序的可加工机器数等数据对得上就能运行。

  • 3、参考文献:

[1]赵小惠,卫艳芳,王凯峰等.改进蚁群算法的柔性作业车间调度问题研究[J].组合机床与自动化加工技术,2022(02):165-168.DOI:10.13462/j.cnki.mmtamt.2022.02.038.

六、代码

有4个py文件和一个mk01的excel文件:

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

演示视频:

论文复现丨蚁群算法求解柔性车间调度问题

文末
完整算法源码+数据:见下方微信公众号:关注后回复:车间调度

# 微信公众号:学长带你飞
# 主要更新方向:1、柔性车间调度问题求解算法
#              2、学术写作技巧
#              3、读书感悟
# @Author  : Jack Hao
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值