文章复现: guo_integrated_2012
This commit is contained in:
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.idea
|
||||||
|
result/
|
||||||
|
data/
|
||||||
|
*.txt
|
||||||
|
__pycache__
|
||||||
|
Lib/
|
||||||
|
Scripts/
|
||||||
|
*.cfg
|
34
SMO/benchmarks.py
Normal file
34
SMO/benchmarks.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Python code of Spider-Monkey Optimization (SMO)
|
||||||
|
Coded by: Mukesh Saraswat (emailid: saraswatmukesh@gmail.com), Himanshu Mittal (emailid: himanshu.mittal224@gmail.com) and Raju Pal (emailid: raju3131.pal@gmail.com)
|
||||||
|
The code template used is similar to code given at link: https://github.com/himanshuRepo/CKGSA-in-Python
|
||||||
|
and C++ version of the SMO at link: http://smo.scrs.in/
|
||||||
|
|
||||||
|
Reference: Jagdish Chand Bansal, Harish Sharma, Shimpi Singh Jadon, and Maurice Clerc. "Spider monkey optimization algorithm for numerical optimization." Memetic computing 6, no. 1, 31-47, 2014.
|
||||||
|
@link: http://smo.scrs.in/
|
||||||
|
|
||||||
|
-- Benchmark.py: Defining the benchmark function along its range lower bound, upper bound and dimensions
|
||||||
|
|
||||||
|
Code compatible:
|
||||||
|
-- Python: 2.* or 3.*
|
||||||
|
"""
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
|
# define the function blocks
|
||||||
|
def F1(x):
|
||||||
|
s = numpy.sum(x ** 2);
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
# define the function parameters
|
||||||
|
def getFunctionDetails():
|
||||||
|
# [name, lb, ub, dim, acc_err, obj_val]
|
||||||
|
param = ["F1", -100, 100, 30, 1.0e-5, 0]
|
||||||
|
return param
|
||||||
|
|
||||||
|
|
||||||
|
|
79
SMO/main.py
Normal file
79
SMO/main.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Python code of Spider-Monkey Optimization (SMO)
|
||||||
|
Coded by: Mukesh Saraswat (emailid: saraswatmukesh@gmail.com), Himanshu Mittal (emailid: himanshu.mittal224@gmail.com) and Raju Pal (emailid: raju3131.pal@gmail.com)
|
||||||
|
The code template used is similar to code given at link: https://github.com/himanshuRepo/CKGSA-in-Python
|
||||||
|
and C++ version of the SMO at link: http://smo.scrs.in/
|
||||||
|
|
||||||
|
Reference: Jagdish Chand Bansal, Harish Sharma, Shimpi Singh Jadon, and Maurice Clerc. "Spider monkey optimization algorithm for numerical optimization." Memetic computing 6, no. 1, 31-47, 2014.
|
||||||
|
@link: http://smo.scrs.in/
|
||||||
|
|
||||||
|
-- Main.py: Calling the Spider-Monkey Optimization (SMO) Algorithm
|
||||||
|
for minimizing of an objective Function
|
||||||
|
|
||||||
|
Code compatible:
|
||||||
|
-- Python: 2.* or 3.*
|
||||||
|
"""
|
||||||
|
import smo
|
||||||
|
import benchmarks
|
||||||
|
import csv
|
||||||
|
import numpy
|
||||||
|
import time
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
|
def selector(func_details, popSize, Iter, succ_rate, mean_feval):
|
||||||
|
function_name = func_details[0]
|
||||||
|
lb = func_details[1]
|
||||||
|
ub = func_details[2]
|
||||||
|
dim = func_details[3]
|
||||||
|
acc_err = func_details[4]
|
||||||
|
obj_val = func_details[5]
|
||||||
|
|
||||||
|
|
||||||
|
x, succ_rate, mean_feval = smo.main(getattr(benchmarks, function_name), lb, ub, dim, popSize, Iter, acc_err,
|
||||||
|
obj_val, succ_rate, mean_feval)
|
||||||
|
return x, succ_rate, mean_feval
|
||||||
|
|
||||||
|
|
||||||
|
# Select number of repetitions for each experiment.
|
||||||
|
# To obtain meaningful statistical results, usually 30 independent runs are executed for each algorithm.
|
||||||
|
NumOfRuns = 2
|
||||||
|
|
||||||
|
# Select general parameters for all optimizers (population size, number of iterations)
|
||||||
|
PopulationSize = 10
|
||||||
|
Iterations = 500
|
||||||
|
|
||||||
|
mean_error = 0
|
||||||
|
total_feval = 0
|
||||||
|
mean1 = 0
|
||||||
|
var = 0
|
||||||
|
sd = 0
|
||||||
|
mean_feval = 0
|
||||||
|
succ_rate = 0
|
||||||
|
GlobalMins = numpy.zeros(NumOfRuns)
|
||||||
|
|
||||||
|
for k in range(0, NumOfRuns):
|
||||||
|
|
||||||
|
func_details = benchmarks.getFunctionDetails()
|
||||||
|
print("Run: {}".format(k + 1))
|
||||||
|
x, succ_rate, mean_feval = selector(func_details, PopulationSize, Iterations, succ_rate, mean_feval)
|
||||||
|
mean_error = mean_error + x.error;
|
||||||
|
mean1 = mean1 + x.convergence[-1]
|
||||||
|
total_feval = total_feval + x.feval
|
||||||
|
GlobalMins[k] = x.convergence[-1]
|
||||||
|
|
||||||
|
|
||||||
|
mean1 = mean1 / NumOfRuns;
|
||||||
|
mean_error = mean_error / NumOfRuns
|
||||||
|
if (succ_rate > 0):
|
||||||
|
mean_feval = mean_feval / succ_rate
|
||||||
|
total_feval = total_feval / NumOfRuns
|
||||||
|
for k in range(NumOfRuns):
|
||||||
|
var = var + math.pow((GlobalMins[k] - mean1), 2)
|
||||||
|
var = var / NumOfRuns
|
||||||
|
sd = math.sqrt(var)
|
||||||
|
|
||||||
|
print(
|
||||||
|
"Values after executing SMO: \n Mean Error:{} \n Mean Function eval:{} \n Total Function eval:{} \n Variance:{} \n STD:{}".format(
|
||||||
|
mean_error, mean_feval, total_feval, var, sd))
|
348
SMO/smo.py
Normal file
348
SMO/smo.py
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Python code of Spider-Monkey Optimization (SMO)
|
||||||
|
Coded by: Mukesh Saraswat (emailid: saraswatmukesh@gmail.com), Himanshu Mittal (emailid: himanshu.mittal224@gmail.com) and Raju Pal (emailid: raju3131.pal@gmail.com)
|
||||||
|
The code template used is similar to code given at link: https://github.com/himanshuRepo/CKGSA-in-Python
|
||||||
|
and C++ version of the SMO at link: http://smo.scrs.in/
|
||||||
|
|
||||||
|
Reference: Jagdish Chand Bansal, Harish Sharma, Shimpi Singh Jadon, and Maurice Clerc. "Spider monkey optimization algorithm for numerical optimization." Memetic computing 6, no. 1, 31-47, 2014.
|
||||||
|
@link: http://smo.scrs.in/
|
||||||
|
|
||||||
|
-- smo.py: Performing the Spider-Monkey Optimization (SMO) Algorithm
|
||||||
|
|
||||||
|
Code compatible:
|
||||||
|
-- Python: 2.* or 3.*
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import division
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import numpy
|
||||||
|
import math
|
||||||
|
from solution import solution
|
||||||
|
|
||||||
|
|
||||||
|
class SMO():
|
||||||
|
def __init__(self, objf1, lb1, ub1, dim1, PopSize1, acc_err1, iters1):
|
||||||
|
self.PopSize = PopSize1
|
||||||
|
self.dim = dim1
|
||||||
|
self.acc_err = acc_err1
|
||||||
|
self.lb = lb1
|
||||||
|
self.ub = ub1
|
||||||
|
self.objf = objf1
|
||||||
|
self.pos = numpy.zeros((PopSize1, dim1))
|
||||||
|
self.fun_val = numpy.zeros(PopSize1)
|
||||||
|
self.fitness = numpy.zeros(PopSize1)
|
||||||
|
self.gpoint = numpy.zeros((PopSize1, 2))
|
||||||
|
self.prob = numpy.zeros(PopSize1)
|
||||||
|
self.LocalLimit = dim1 * PopSize1;
|
||||||
|
self.GlobalLimit = PopSize1;
|
||||||
|
self.fit = numpy.zeros(PopSize1)
|
||||||
|
self.MinCost = numpy.zeros(iters1)
|
||||||
|
self.Bestpos = numpy.zeros(dim1)
|
||||||
|
self.group = 0
|
||||||
|
self.func_eval = 0
|
||||||
|
self.part = 1
|
||||||
|
self.max_part = 5
|
||||||
|
self.cr = 0.1
|
||||||
|
|
||||||
|
# ====== Function: CalculateFitness() ========= #
|
||||||
|
def CalculateFitness(self, fun1):
|
||||||
|
if fun1 >= 0:
|
||||||
|
result = (1 / (fun1 + 1))
|
||||||
|
else:
|
||||||
|
result = (1 + math.fabs(fun1))
|
||||||
|
return result
|
||||||
|
|
||||||
|
# ================ X X X ===================== #
|
||||||
|
|
||||||
|
# ==================================== Function: Initialization() ============================================ #
|
||||||
|
def initialize(self):
|
||||||
|
global GlobalMin, GlobalLeaderPosition, GlobalLimitCount, LocalMin, LocalLimitCount, LocalLeaderPosition
|
||||||
|
S_max = int(self.PopSize / 2)
|
||||||
|
LocalMin = numpy.zeros(S_max)
|
||||||
|
LocalLeaderPosition = numpy.zeros((S_max, self.dim))
|
||||||
|
LocalLimitCount = numpy.zeros(S_max)
|
||||||
|
for i in range(self.PopSize):
|
||||||
|
for j in range(self.dim):
|
||||||
|
if type(self.ub) == int:
|
||||||
|
self.pos[i, j] = random.random() * (self.ub - self.lb) + self.lb
|
||||||
|
else:
|
||||||
|
self.pos[i, j] = random.random() * (self.ub[j] - self.lb[j]) + self.lb[j]
|
||||||
|
# Calculate objective function for each particle
|
||||||
|
for i in range(self.PopSize):
|
||||||
|
# Performing the bound checking
|
||||||
|
self.pos[i, :] = numpy.clip(self.pos[i, :], self.lb, self.ub)
|
||||||
|
self.fun_val[i] = self.objf(self.pos[i, :])
|
||||||
|
self.func_eval += 1
|
||||||
|
self.fitness[i] = self.CalculateFitness(self.fun_val[i])
|
||||||
|
|
||||||
|
# Initialize Global Leader Learning
|
||||||
|
GlobalMin = self.fun_val[0]
|
||||||
|
GlobalLeaderPosition = self.pos[0, :]
|
||||||
|
GlobalLimitCount = 0
|
||||||
|
|
||||||
|
# Initialize Local Leader Learning
|
||||||
|
for k in range(self.group):
|
||||||
|
LocalMin[k] = self.fun_val[int(self.gpoint[k, 0])]
|
||||||
|
LocalLimitCount[k] = 0
|
||||||
|
LocalLeaderPosition[k, :] = self.pos[int(self.gpoint[k, 0]), :]
|
||||||
|
|
||||||
|
# ============================================ X X X ======================================================= #
|
||||||
|
|
||||||
|
# =========== Function: CalculateProbabilities() ============ #
|
||||||
|
def CalculateProbabilities(self):
|
||||||
|
maxfit = self.fitness[0];
|
||||||
|
i = 1
|
||||||
|
while (i < self.PopSize):
|
||||||
|
if (self.fitness[i] > maxfit):
|
||||||
|
maxfit = self.fitness[i];
|
||||||
|
i += 1
|
||||||
|
for i in range(self.PopSize):
|
||||||
|
self.prob[i] = (0.9 * (self.fitness[i] / maxfit)) + 0.1;
|
||||||
|
|
||||||
|
# ========================== X X X ======================== #
|
||||||
|
|
||||||
|
# ================= Function: create_group() ================ #
|
||||||
|
def create_group(self):
|
||||||
|
g = 0
|
||||||
|
lo = 0
|
||||||
|
while (lo < self.PopSize):
|
||||||
|
hi = lo + int(self.PopSize / self.part)
|
||||||
|
self.gpoint[g, 0] = lo
|
||||||
|
self.gpoint[g, 1] = hi
|
||||||
|
if ((self.PopSize - hi) < (int(self.PopSize / self.part))):
|
||||||
|
self.gpoint[g, 1] = (self.PopSize - 1)
|
||||||
|
g = g + 1
|
||||||
|
lo = hi + 1
|
||||||
|
self.group = g
|
||||||
|
|
||||||
|
# ========================== X X X ======================== #
|
||||||
|
|
||||||
|
# ================= Function: LocalLearning() ================ #
|
||||||
|
def LocalLearning(self):
|
||||||
|
global LocalMin, LocalLimitCount, LocalLeaderPosition
|
||||||
|
S_max = int(self.PopSize / 2)
|
||||||
|
OldMin = numpy.zeros(S_max)
|
||||||
|
for k in range(self.group):
|
||||||
|
OldMin[k] = LocalMin[k]
|
||||||
|
|
||||||
|
for k in range(self.group):
|
||||||
|
i = int(self.gpoint[k, 0])
|
||||||
|
while (i <= int(self.gpoint[k, 1])):
|
||||||
|
if (self.fun_val[i] < LocalMin[k]):
|
||||||
|
LocalMin[k] = self.fun_val[i]
|
||||||
|
LocalLeaderPosition[k, :] = self.pos[i, :]
|
||||||
|
i = i + 1
|
||||||
|
|
||||||
|
for k in range(self.group):
|
||||||
|
if (math.fabs(OldMin[k] - LocalMin[k]) < self.acc_err):
|
||||||
|
LocalLimitCount[k] = LocalLimitCount[k] + 1
|
||||||
|
else:
|
||||||
|
LocalLimitCount[k] = 0
|
||||||
|
|
||||||
|
# ========================== X X X ======================== #
|
||||||
|
|
||||||
|
# ================= Function: GlobalLearning() ================ #
|
||||||
|
def GlobalLearning(self):
|
||||||
|
global GlobalMin, GlobalLeaderPosition, GlobalLimitCount
|
||||||
|
G_trial = GlobalMin
|
||||||
|
for i in range(self.PopSize):
|
||||||
|
if (self.fun_val[i] < GlobalMin):
|
||||||
|
GlobalMin = self.fun_val[i]
|
||||||
|
GlobalLeaderPosition = self.pos[i, :]
|
||||||
|
|
||||||
|
if (math.fabs(G_trial - GlobalMin) < self.acc_err):
|
||||||
|
GlobalLimitCount = GlobalLimitCount + 1
|
||||||
|
else:
|
||||||
|
GlobalLimitCount = 0
|
||||||
|
|
||||||
|
# ========================== X X X ======================== #
|
||||||
|
|
||||||
|
# ================= Function: LocalLeaderPhase() ================ #
|
||||||
|
def LocalLeaderPhase(self, k):
|
||||||
|
global LocalLeaderPosition
|
||||||
|
new_position = numpy.zeros((1, self.dim))
|
||||||
|
lo = int(self.gpoint[k, 0])
|
||||||
|
hi = int(self.gpoint[k, 1])
|
||||||
|
i = lo
|
||||||
|
while (i <= hi):
|
||||||
|
while True:
|
||||||
|
PopRand = int((random.random() * (hi - lo) + lo))
|
||||||
|
if (PopRand != i):
|
||||||
|
break
|
||||||
|
for j in range(self.dim):
|
||||||
|
if (random.random() >= self.cr):
|
||||||
|
new_position[0, j] = self.pos[i, j] + (LocalLeaderPosition[k, j] - self.pos[i, j]) * (
|
||||||
|
random.random()) + (self.pos[PopRand, j] - self.pos[i, j]) * (random.random() - 0.5) * 2
|
||||||
|
else:
|
||||||
|
new_position[0, j] = self.pos[i, j]
|
||||||
|
new_position = numpy.clip(new_position, self.lb, self.ub)
|
||||||
|
|
||||||
|
ObjValSol = self.objf(new_position)
|
||||||
|
self.func_eval += 1
|
||||||
|
FitnessSol = self.CalculateFitness(ObjValSol)
|
||||||
|
if (FitnessSol > self.fitness[i]):
|
||||||
|
self.pos[i, :] = new_position
|
||||||
|
self.fun_val[i] = ObjValSol
|
||||||
|
self.fitness[i] = FitnessSol
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
# ========================== X X X ======================== #
|
||||||
|
|
||||||
|
# ================= Function: GlobalLeaderPhase() ================ #
|
||||||
|
def GlobalLeaderPhase(self, k):
|
||||||
|
global GlobalLeaderPosition
|
||||||
|
new_position = numpy.zeros((1, self.dim))
|
||||||
|
lo = int(self.gpoint[k, 0])
|
||||||
|
hi = int(self.gpoint[k, 1])
|
||||||
|
i = lo;
|
||||||
|
l = lo;
|
||||||
|
while (l < hi):
|
||||||
|
if (random.random() < self.prob[i]):
|
||||||
|
l += 1
|
||||||
|
while True:
|
||||||
|
PopRand = int(random.random() * (hi - lo) + lo)
|
||||||
|
if (PopRand != i):
|
||||||
|
break
|
||||||
|
param2change = int(random.random() * self.dim)
|
||||||
|
new_position = self.pos[i, :]
|
||||||
|
new_position[param2change] = self.pos[i, param2change] + (
|
||||||
|
GlobalLeaderPosition[param2change] - self.pos[i, param2change]) * (random.random()) + (
|
||||||
|
self.pos[PopRand, param2change] - self.pos[
|
||||||
|
i, param2change]) * (random.random() - 0.5) * 2
|
||||||
|
new_position = numpy.clip(new_position, self.lb, self.ub)
|
||||||
|
ObjValSol = self.objf(new_position)
|
||||||
|
self.func_eval += 1
|
||||||
|
FitnessSol = self.CalculateFitness(ObjValSol)
|
||||||
|
if (FitnessSol > self.fitness[i]):
|
||||||
|
self.pos[i, :] = new_position
|
||||||
|
self.fun_val[i] = ObjValSol
|
||||||
|
self.fitness[i] = FitnessSol
|
||||||
|
i += 1;
|
||||||
|
if (i == hi):
|
||||||
|
i = lo;
|
||||||
|
|
||||||
|
# ========================== X X X ======================== #
|
||||||
|
|
||||||
|
# ================= Function: GlobalLeaderDecision() ================ #
|
||||||
|
def GlobalLeaderDecision(self):
|
||||||
|
global GlobalLimitCount
|
||||||
|
if (GlobalLimitCount > self.GlobalLimit):
|
||||||
|
GlobalLimitCount = 0
|
||||||
|
if (self.part < self.max_part):
|
||||||
|
self.part = self.part + 1
|
||||||
|
self.create_group()
|
||||||
|
self.LocalLearning()
|
||||||
|
else:
|
||||||
|
self.part = 1
|
||||||
|
self.create_group()
|
||||||
|
self.LocalLearning()
|
||||||
|
|
||||||
|
# ========================== X X X ======================== #
|
||||||
|
|
||||||
|
# ================= Function: LocalLeaderDecision() ================ #
|
||||||
|
def LocalLeaderDecision(self):
|
||||||
|
global GlobalLeaderPosition, LocalLimitCount, LocalLeaderPosition
|
||||||
|
for k in range(self.group):
|
||||||
|
if (LocalLimitCount[k] > self.LocalLimit):
|
||||||
|
i = self.gpoint[k, 0]
|
||||||
|
while (i <= int(self.gpoint[k, 1])):
|
||||||
|
for j in range(self.dim):
|
||||||
|
if (random.random() >= self.cr):
|
||||||
|
if type(self.ub) == int:
|
||||||
|
self.pos[i, j] = random.random() * (self.ub - self.lb) + self.lb
|
||||||
|
else:
|
||||||
|
self.pos[i, j] = random.random() * (self.ub[j] - self.lb[j]) + self.lb[j]
|
||||||
|
else:
|
||||||
|
self.pos[i, j] = self.pos[i, j] + (
|
||||||
|
GlobalLeaderPosition[j] - self.pos[i, j]) * random.random() + (
|
||||||
|
self.pos[i, j] - LocalLeaderPosition[k, j]) * random.random()
|
||||||
|
self.pos[i, :] = numpy.clip(self.pos[i, :], self.lb, self.ub)
|
||||||
|
self.fun_val[i] = self.objf(self.pos[i, :])
|
||||||
|
self.func_eval += 1
|
||||||
|
self.fitness[i] = self.CalculateFitness(self.fun_val[i])
|
||||||
|
i += 1
|
||||||
|
LocalLimitCount[k] = 0
|
||||||
|
# ========================== X X X ======================== #
|
||||||
|
|
||||||
|
|
||||||
|
# ==================================== Main() ===================================== #
|
||||||
|
def main(objf1, lb1, ub1, dim1, PopSize1, iters, acc_err1, obj_val, succ_rate, mean_feval):
|
||||||
|
smo = SMO(objf1, lb1, ub1, dim1, PopSize1, acc_err1, iters)
|
||||||
|
s = solution()
|
||||||
|
print("SMO is optimizing \"" + smo.objf.__name__ + "\"")
|
||||||
|
timerStart = time.time()
|
||||||
|
s.startTime = time.strftime("%Y-%m-%d-%H-%M-%S")
|
||||||
|
|
||||||
|
# =========================== Calling: initialize() =========================== #
|
||||||
|
smo.initialize()
|
||||||
|
|
||||||
|
# ========================== Calling: GlobalLearning() ======================== #
|
||||||
|
smo.GlobalLearning()
|
||||||
|
|
||||||
|
# ========================= Calling: LocalLearning() ========================== #
|
||||||
|
smo.LocalLearning()
|
||||||
|
|
||||||
|
# ========================== Calling: create_group() ========================== #
|
||||||
|
smo.create_group()
|
||||||
|
|
||||||
|
# ================================= Looping ================================== #
|
||||||
|
for l in range(iters):
|
||||||
|
for k in range(smo.group):
|
||||||
|
# ==================== Calling: LocalLeaderPhase() =================== #
|
||||||
|
smo.LocalLeaderPhase(k)
|
||||||
|
|
||||||
|
# =================== Calling: CalculateProbabilities() ================== #
|
||||||
|
smo.CalculateProbabilities()
|
||||||
|
|
||||||
|
for k in range(smo.group):
|
||||||
|
# ==================== Calling: GlobalLeaderPhase() ================== #
|
||||||
|
smo.GlobalLeaderPhase(k)
|
||||||
|
|
||||||
|
# ======================= Calling: GlobalLearning() ====================== #
|
||||||
|
smo.GlobalLearning()
|
||||||
|
|
||||||
|
# ======================= Calling: LocalLearning() ======================= #
|
||||||
|
smo.LocalLearning()
|
||||||
|
|
||||||
|
# ================== Calling: LocalLeaderDecision() ====================== #
|
||||||
|
smo.LocalLeaderDecision()
|
||||||
|
|
||||||
|
# ===================== Calling: GlobalLeaderDecision() ================== #
|
||||||
|
smo.GlobalLeaderDecision()
|
||||||
|
|
||||||
|
# ======================= Updating: 'cr' parameter ======================= #
|
||||||
|
smo.cr = smo.cr + (0.4 / iters)
|
||||||
|
|
||||||
|
# ====================== Saving the best individual ====================== #
|
||||||
|
smo.MinCost[l] = GlobalMin
|
||||||
|
gBestScore = GlobalMin
|
||||||
|
|
||||||
|
# ================ Displaying the fitness of each iteration ============== #
|
||||||
|
if (l % 1 == 0):
|
||||||
|
print(['At iteration ' + str(l + 1) + ' the best fitness is ' + str(gBestScore)]);
|
||||||
|
|
||||||
|
# ====================== Checking: acc_error ============================ #
|
||||||
|
if (math.fabs(GlobalMin - obj_val) <= smo.acc_err):
|
||||||
|
succ_rate += 1
|
||||||
|
mean_feval = mean_feval + smo.func_eval
|
||||||
|
break
|
||||||
|
# ========================= XXX Ending of Loop XXX ========================== #
|
||||||
|
|
||||||
|
# =========================== XX Result saving XX =========================== #
|
||||||
|
error1 = math.fabs(GlobalMin - obj_val)
|
||||||
|
timerEnd = time.time()
|
||||||
|
s.endTime = time.strftime("%Y-%m-%d-%H-%M-%S")
|
||||||
|
s.executionTime = timerEnd - timerStart
|
||||||
|
s.convergence = smo.MinCost
|
||||||
|
s.optimizer = "SMO"
|
||||||
|
s.error = error1
|
||||||
|
s.feval = smo.func_eval
|
||||||
|
s.objfname = smo.objf.__name__
|
||||||
|
|
||||||
|
return s, succ_rate, mean_feval
|
||||||
|
|
||||||
|
# ================================ X X X =================================== #
|
||||||
|
|
||||||
|
|
33
SMO/solution.py
Normal file
33
SMO/solution.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Python code of Spider-Monkey Optimization (SMO)
|
||||||
|
Coded by: Mukesh Saraswat (emailid: saraswatmukesh@gmail.com), Himanshu Mittal (emailid: himanshu.mittal224@gmail.com) and Raju Pal (emailid: raju3131.pal@gmail.com)
|
||||||
|
The code template used is similar to code given at link: https://github.com/himanshuRepo/CKGSA-in-Python
|
||||||
|
and C++ version of the SMO at link: http://smo.scrs.in/
|
||||||
|
|
||||||
|
Reference: Jagdish Chand Bansal, Harish Sharma, Shimpi Singh Jadon, and Maurice Clerc. "Spider monkey optimization algorithm for numerical optimization." Memetic computing 6, no. 1, 31-47, 2014.
|
||||||
|
@link: http://smo.scrs.in/
|
||||||
|
|
||||||
|
-- solution.py: Defining the solution variable for saving the output variables
|
||||||
|
|
||||||
|
Code compatible:
|
||||||
|
-- Python: 2.* or 3.*
|
||||||
|
"""
|
||||||
|
|
||||||
|
class solution:
|
||||||
|
def __init__(self):
|
||||||
|
self.best = 0
|
||||||
|
self.bestIndividual=[]
|
||||||
|
self.convergence = []
|
||||||
|
self.optimizer=""
|
||||||
|
self.objfname=""
|
||||||
|
self.startTime=0
|
||||||
|
self.endTime=0
|
||||||
|
self.executionTime=0
|
||||||
|
self.lb=0
|
||||||
|
self.ub=0
|
||||||
|
self.dim=0
|
||||||
|
self.popnum=0
|
||||||
|
self.error =0
|
||||||
|
self.feval=0
|
||||||
|
self.maxiers=0
|
89
dataloader.py
Normal file
89
dataloader.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
from optimizer_common import *
|
||||||
|
|
||||||
|
|
||||||
|
def load_data(filename: str, load_cp_data=True, load_feeder_data=True, component_register=False):
|
||||||
|
# 锁定随机数种子
|
||||||
|
random.seed(0)
|
||||||
|
|
||||||
|
# 读取PCB数据
|
||||||
|
filename = 'data/' + filename
|
||||||
|
pcb_data = pd.DataFrame(pd.read_csv(filepath_or_buffer=filename, sep='\t', header=None))
|
||||||
|
if len(pcb_data.columns) <= 17:
|
||||||
|
step_col = ["ref", "x", "y", "z", "r", "part", "desc", "fdr", "nz", "hd", "cs", "cy", "sk", "bl", "ar",
|
||||||
|
"pl", "lv"]
|
||||||
|
elif len(pcb_data.columns) <= 18:
|
||||||
|
step_col = ["ref", "x", "y", "z", "r", "part", "desc", "fdr", "nz", "hd", "cs", "cy", "sk", "bl", "ar", "fid",
|
||||||
|
"pl", "lv"]
|
||||||
|
else:
|
||||||
|
step_col = ["ref", "x", "y", "z", "r", "part", "desc", "fdr", "nz", "hd", "cs", "cy", "sk", "bl", "ar", "fid",
|
||||||
|
"", "pl", "lv"]
|
||||||
|
|
||||||
|
pcb_data.columns = step_col
|
||||||
|
pcb_data = pcb_data.dropna(axis=1)
|
||||||
|
|
||||||
|
# 坐标系处理
|
||||||
|
# pcb_data = pcb_data.sort_values(by = ['x', 'y'], ascending = True)
|
||||||
|
# pcb_data["x"] = pcb_data["x"].apply(lambda x: -x)
|
||||||
|
|
||||||
|
# 注册元件检查
|
||||||
|
component_data = None
|
||||||
|
if load_cp_data:
|
||||||
|
part_feeder_assign = defaultdict(set)
|
||||||
|
part_col = ["part", "desc", "fdr", "nz", 'camera', 'group', 'feeder-limit']
|
||||||
|
try:
|
||||||
|
component_data = pd.DataFrame(pd.read_csv(filepath_or_buffer='component.txt', sep='\t', header=None), columns=part_col)
|
||||||
|
except:
|
||||||
|
component_data = pd.DataFrame(columns=part_col)
|
||||||
|
for _, data in pcb_data.iterrows():
|
||||||
|
part, nozzle = data.part, data.nz.split(' ')[1]
|
||||||
|
slot = data['fdr'].split(' ')[0]
|
||||||
|
|
||||||
|
if part not in component_data['part'].values:
|
||||||
|
if not component_register:
|
||||||
|
raise Exception("unregistered component: " + component_data['part'].values)
|
||||||
|
else:
|
||||||
|
component_data = pd.concat([component_data,
|
||||||
|
pd.DataFrame([part, '', 'SM8', nozzle, '飞行相机1', 'CHIP-Rect', 0],
|
||||||
|
index=part_col).T], ignore_index=True)
|
||||||
|
# warning_info = 'register component ' + part + ' with default feeder type'
|
||||||
|
# warnings.warn(warning_info, UserWarning)
|
||||||
|
part_index = component_data[component_data['part'] == part].index.tolist()[0]
|
||||||
|
part_feeder_assign[part].add(slot)
|
||||||
|
|
||||||
|
if nozzle != 'A' and component_data.loc[part_index]['nz'] != nozzle:
|
||||||
|
warning_info = 'the nozzle type of component ' + part + ' is not consistent with the pcb data'
|
||||||
|
warnings.warn(warning_info, UserWarning)
|
||||||
|
|
||||||
|
for idx, data in component_data.iterrows():
|
||||||
|
if data['fdr'][0:3] == 'SME': # 电动供料器和气动供料器参数一致
|
||||||
|
component_data.at[idx, 'fdr'] = data['fdr'][0:2] + data['fdr'][3:]
|
||||||
|
|
||||||
|
for part, slots in part_feeder_assign.items():
|
||||||
|
part_index = component_data[component_data['part'] == part].index.tolist()[0]
|
||||||
|
component_data.at[part_index, 'feeder-limit'] = max(len(slots), component_data.at[part_index, 'feeder-limit'])
|
||||||
|
|
||||||
|
# 读取供料器基座数据
|
||||||
|
feeder_data = pd.DataFrame(columns=range(3))
|
||||||
|
if load_feeder_data:
|
||||||
|
for data in pcb_data.iterrows():
|
||||||
|
fdr = data[1]['fdr']
|
||||||
|
slot, part = fdr.split(' ')
|
||||||
|
if slot[0] != 'F' and slot[0] != 'R':
|
||||||
|
continue
|
||||||
|
slot = int(slot[1:]) if slot[0] == 'F' else int(slot[1:]) + max_slot_index // 2
|
||||||
|
feeder_data = pd.concat([feeder_data, pd.DataFrame([slot, part, 1]).T])
|
||||||
|
|
||||||
|
feeder_data.columns = ['slot', 'part', 'arg'] # arg表示是否为预分配,不表示分配数目
|
||||||
|
feeder_data.drop_duplicates(subset='slot', inplace=True, ignore_index=True)
|
||||||
|
# 随机移除部分已安装的供料器
|
||||||
|
if load_feeder_data == 2:
|
||||||
|
drop_index = random.sample(list(range(len(feeder_data))), len(feeder_data) // 2)
|
||||||
|
feeder_data.drop(index=drop_index, inplace=True)
|
||||||
|
|
||||||
|
feeder_data.sort_values(by='slot', ascending=True, inplace=True, ignore_index=True)
|
||||||
|
else:
|
||||||
|
feeder_data.columns = ['slot', 'part', 'arg'] # 同上
|
||||||
|
|
||||||
|
return pcb_data, component_data, feeder_data
|
316
optimizer.py
Normal file
316
optimizer.py
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
import math
|
||||||
|
import random
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
from optimizer_common import *
|
||||||
|
from dataloader import *
|
||||||
|
|
||||||
|
|
||||||
|
def get_top_k_value(pop_val, k: int):
|
||||||
|
res = []
|
||||||
|
pop_val_cpy = copy.deepcopy(pop_val)
|
||||||
|
pop_val_cpy.sort(reverse=True)
|
||||||
|
|
||||||
|
for i in range(min(len(pop_val_cpy), k)):
|
||||||
|
for j in range(len(pop_val)):
|
||||||
|
if abs(pop_val_cpy[i] - pop_val[j]) < 1e-9 and j not in res:
|
||||||
|
res.append(j)
|
||||||
|
break
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def swap_mutation(component_points, individual):
|
||||||
|
offspring = individual.copy()
|
||||||
|
|
||||||
|
idx, component_index = 0, random.randint(0, len(component_points) - 1)
|
||||||
|
for points in component_points.values():
|
||||||
|
if component_index == 0:
|
||||||
|
index1 = random.randint(0, points + max_machine_index - 2)
|
||||||
|
while True:
|
||||||
|
index2 = random.randint(0, points + max_machine_index - 2)
|
||||||
|
if index1 != index2 and offspring[idx + index1] != offspring[idx + index2]:
|
||||||
|
break
|
||||||
|
offspring[idx + index1], offspring[idx + index2] = offspring[idx + index2], offspring[idx + index1]
|
||||||
|
break
|
||||||
|
|
||||||
|
component_index -= 1
|
||||||
|
idx += (points + max_machine_index - 1)
|
||||||
|
|
||||||
|
return offspring
|
||||||
|
|
||||||
|
|
||||||
|
def roulette_wheel_selection(pop_eval):
|
||||||
|
# Roulette wheel
|
||||||
|
random_val = np.random.random()
|
||||||
|
for idx, val in enumerate(pop_eval):
|
||||||
|
random_val -= val
|
||||||
|
if random_val <= 0:
|
||||||
|
return idx
|
||||||
|
return len(pop_eval) - 1
|
||||||
|
|
||||||
|
|
||||||
|
def random_selective(data, possibility): # 依概率选择随机数
|
||||||
|
assert len(data) == len(possibility) and len(data) > 0
|
||||||
|
|
||||||
|
sum_val = sum(possibility)
|
||||||
|
possibility = [p / sum_val for p in possibility]
|
||||||
|
|
||||||
|
random_val = random.random()
|
||||||
|
for idx, val in enumerate(possibility):
|
||||||
|
random_val -= val
|
||||||
|
if random_val <= 0:
|
||||||
|
break
|
||||||
|
return data[idx]
|
||||||
|
|
||||||
|
|
||||||
|
def selective_initialization(component_points, population_size):
|
||||||
|
population = [] # population initialization
|
||||||
|
|
||||||
|
for _ in range(population_size):
|
||||||
|
individual = []
|
||||||
|
for points in component_points.values():
|
||||||
|
if points == 0:
|
||||||
|
continue
|
||||||
|
avl_machine_num = random.randint(1, min(max_machine_index, points)) # 可用机器数
|
||||||
|
|
||||||
|
selective_possibility = []
|
||||||
|
for p in range(1, avl_machine_num + 1):
|
||||||
|
selective_possibility.append(pow(2, avl_machine_num - p + 1))
|
||||||
|
|
||||||
|
sel_machine_num = random_selective([p + 1 for p in range(avl_machine_num)], selective_possibility) # 选择的机器数
|
||||||
|
sel_machine_set = random.sample([p for p in range(avl_machine_num)], sel_machine_num)
|
||||||
|
|
||||||
|
sel_machine_points = [1 for _ in range(sel_machine_num)]
|
||||||
|
for p in range(sel_machine_num - 1):
|
||||||
|
if points == sum(sel_machine_points):
|
||||||
|
break
|
||||||
|
assign_points = random.randint(1, points - sum(sel_machine_points))
|
||||||
|
sel_machine_points[p] += assign_points
|
||||||
|
|
||||||
|
if sum(sel_machine_points) < points:
|
||||||
|
sel_machine_points[-1] += (points - sum(sel_machine_points))
|
||||||
|
|
||||||
|
# code component allocation into chromosome
|
||||||
|
for p in range(max_machine_index):
|
||||||
|
if p in sel_machine_set:
|
||||||
|
individual += [0 for _ in range(sel_machine_points[0])]
|
||||||
|
sel_machine_points.pop(0)
|
||||||
|
individual.append(1)
|
||||||
|
individual.pop(-1)
|
||||||
|
|
||||||
|
population.append(individual)
|
||||||
|
return population
|
||||||
|
|
||||||
|
|
||||||
|
def selective_crossover(mother, father, non_decelerating=True):
|
||||||
|
assert len(mother) == len(father)
|
||||||
|
|
||||||
|
offspring1, offspring2 = mother.copy(), father.copy()
|
||||||
|
one_counter, feasible_cutline = 0, []
|
||||||
|
for idx in range(len(mother) - 1):
|
||||||
|
if mother[idx] == 1:
|
||||||
|
one_counter += 1
|
||||||
|
if father[idx] == 1:
|
||||||
|
one_counter -= 1
|
||||||
|
|
||||||
|
# first constraint: the total number of “1”s (the number of partitions) in the chromosome is unchanged
|
||||||
|
if one_counter != 0 or idx == 0 or idx == len(mother) - 2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# the selected cutline should guarantee there are the same or a larger number unassigned machine
|
||||||
|
# for each component type
|
||||||
|
n_bro, n_new = 0, 0
|
||||||
|
if mother[idx] and mother[idx + 1]:
|
||||||
|
n_bro += 1
|
||||||
|
if father[idx] and father[idx + 1]:
|
||||||
|
n_bro += 1
|
||||||
|
if mother[idx] and father[idx + 1]:
|
||||||
|
n_new += 1
|
||||||
|
if father[idx] and mother[idx + 1]:
|
||||||
|
n_new += 1
|
||||||
|
|
||||||
|
# non_decelerating or accelerating crossover
|
||||||
|
if (non_decelerating and n_bro <= n_new) or n_bro < n_new:
|
||||||
|
feasible_cutline.append(idx)
|
||||||
|
|
||||||
|
if len(feasible_cutline) == 0:
|
||||||
|
return offspring1, offspring2
|
||||||
|
|
||||||
|
cutline_idx = feasible_cutline[random.randint(0, len(feasible_cutline) - 1)]
|
||||||
|
offspring1, offspring2 = mother[:cutline_idx + 1] + father[cutline_idx + 1:], father[:cutline_idx + 1] + mother[
|
||||||
|
cutline_idx + 1:]
|
||||||
|
return offspring1, offspring2
|
||||||
|
|
||||||
|
|
||||||
|
def cal_individual_val(component_points, component_nozzle, individual):
|
||||||
|
idx, objective_val = 0, [0]
|
||||||
|
machine_component_points = [[] for _ in range(max_machine_index)]
|
||||||
|
|
||||||
|
# decode the component allocation
|
||||||
|
for points in component_points.values():
|
||||||
|
component_gene = individual[idx: idx + points + max_machine_index - 1]
|
||||||
|
machine_idx, component_counter = 0, 0
|
||||||
|
for gene in component_gene:
|
||||||
|
if gene:
|
||||||
|
machine_component_points[machine_idx].append(component_counter)
|
||||||
|
machine_idx += 1
|
||||||
|
component_counter = 0
|
||||||
|
else:
|
||||||
|
component_counter += 1
|
||||||
|
machine_component_points[-1].append(component_counter)
|
||||||
|
idx += (points + max_machine_index - 1)
|
||||||
|
|
||||||
|
for machine_idx in range(max_machine_index):
|
||||||
|
nozzle_points = defaultdict(int)
|
||||||
|
for idx, nozzle in component_nozzle.items():
|
||||||
|
if component_points[idx] == 0:
|
||||||
|
continue
|
||||||
|
nozzle_points[nozzle] += machine_component_points[machine_idx][idx]
|
||||||
|
|
||||||
|
machine_points = sum(machine_component_points[machine_idx]) # num of placement points
|
||||||
|
if machine_points == 0:
|
||||||
|
continue
|
||||||
|
ul = math.ceil(len(nozzle_points) * 1.0 / max_head_index) - 1 # num of nozzle set
|
||||||
|
|
||||||
|
# assignments of nozzles to heads
|
||||||
|
wl = 0 # num of workload
|
||||||
|
total_heads = (1 + ul) * max_head_index - len(nozzle_points)
|
||||||
|
nozzle_heads = defaultdict(int)
|
||||||
|
for nozzle in nozzle_points.keys():
|
||||||
|
nozzle_heads[nozzle] = math.floor(nozzle_points[nozzle] * 1.0 / machine_points * total_heads)
|
||||||
|
nozzle_heads[nozzle] += 1
|
||||||
|
|
||||||
|
total_heads = (1 + ul) * max_head_index
|
||||||
|
for heads in nozzle_heads.values():
|
||||||
|
total_heads -= heads
|
||||||
|
|
||||||
|
for nozzle in nozzle_heads.keys(): # TODO:有利于减少周期的方法
|
||||||
|
if total_heads == 0:
|
||||||
|
break
|
||||||
|
nozzle_heads[nozzle] += 1
|
||||||
|
total_heads -= 1
|
||||||
|
|
||||||
|
# averagely assign placements to heads
|
||||||
|
heads_placement = []
|
||||||
|
for nozzle in nozzle_heads.keys():
|
||||||
|
points = math.floor(nozzle_points[nozzle] / nozzle_heads[nozzle])
|
||||||
|
|
||||||
|
heads_placement += [[nozzle, points] for _ in range(nozzle_heads[nozzle])]
|
||||||
|
nozzle_points[nozzle] -= (nozzle_heads[nozzle] * points)
|
||||||
|
for idx in range(len(heads_placement) - 1, -1, -1):
|
||||||
|
if nozzle_points[nozzle] <= 0:
|
||||||
|
break
|
||||||
|
nozzle_points[nozzle] -= 1
|
||||||
|
heads_placement[idx][1] += 1
|
||||||
|
heads_placement = sorted(heads_placement, key=lambda x: x[1], reverse=True)
|
||||||
|
|
||||||
|
# every max_head_index heads in the non-decreasing order are grouped together as nozzle set
|
||||||
|
for idx in range(len(heads_placement) // max_head_index):
|
||||||
|
wl += heads_placement[idx][1]
|
||||||
|
objective_val.append(T_pp * machine_points + T_tr * wl + T_nc * ul)
|
||||||
|
|
||||||
|
return max(objective_val), machine_component_points
|
||||||
|
|
||||||
|
|
||||||
|
@timer_wrapper
|
||||||
|
def optimizer(pcb_data, component_data):
|
||||||
|
# basic parameter
|
||||||
|
# crossover rate & mutation rate: 80% & 10%
|
||||||
|
# population size: 200
|
||||||
|
# the number of generation: 500
|
||||||
|
crossover_rate, mutation_rate = 0.8, 0.1
|
||||||
|
population_size, n_generations = 200, 500
|
||||||
|
|
||||||
|
# the number of placement points and nozzle type of component
|
||||||
|
component_points, component_nozzle = defaultdict(int), defaultdict(str)
|
||||||
|
for data in pcb_data.iterrows():
|
||||||
|
part_index = component_data[component_data['part'] == data[1]['part']].index.tolist()[0]
|
||||||
|
nozzle = component_data.loc[part_index]['nz']
|
||||||
|
|
||||||
|
component_points[part_index] += 1
|
||||||
|
component_nozzle[part_index] = nozzle
|
||||||
|
|
||||||
|
# population initialization
|
||||||
|
best_popval = []
|
||||||
|
population = selective_initialization(component_points, population_size)
|
||||||
|
with tqdm(total=n_generations) as pbar:
|
||||||
|
pbar.set_description('genetic process for PCB assembly')
|
||||||
|
|
||||||
|
new_population, new_pop_val = [], []
|
||||||
|
for _ in range(n_generations):
|
||||||
|
# calculate fitness value
|
||||||
|
pop_val = []
|
||||||
|
for individual in population:
|
||||||
|
val, _ = cal_individual_val(component_points, component_nozzle, individual)
|
||||||
|
pop_val.append(val)
|
||||||
|
|
||||||
|
best_popval.append(min(pop_val))
|
||||||
|
# min-max convert
|
||||||
|
max_val = max(pop_val)
|
||||||
|
pop_val = list(map(lambda v: max_val - v, pop_val))
|
||||||
|
|
||||||
|
sum_pop_val = sum(pop_val)
|
||||||
|
pop_val = [v / sum_pop_val for v in pop_val]
|
||||||
|
|
||||||
|
select_index = get_top_k_value(pop_val, population_size - len(new_pop_val))
|
||||||
|
population = [population[idx] for idx in select_index]
|
||||||
|
pop_val = [pop_val[idx] for idx in select_index]
|
||||||
|
|
||||||
|
population += new_population
|
||||||
|
for individual in new_population:
|
||||||
|
val, _ = cal_individual_val(component_points, component_nozzle, individual)
|
||||||
|
pop_val.append(val)
|
||||||
|
|
||||||
|
# crossover and mutation
|
||||||
|
new_population = []
|
||||||
|
for pop in range(population_size):
|
||||||
|
if pop % 2 == 0 and np.random.random() < crossover_rate:
|
||||||
|
index1 = roulette_wheel_selection(pop_val)
|
||||||
|
while True:
|
||||||
|
index2 = roulette_wheel_selection(pop_val)
|
||||||
|
if index1 != index2:
|
||||||
|
break
|
||||||
|
|
||||||
|
offspring1, offspring2 = selective_crossover(population[index1], population[index2])
|
||||||
|
if np.random.random() < mutation_rate:
|
||||||
|
offspring1 = swap_mutation(component_points, offspring1)
|
||||||
|
|
||||||
|
if np.random.random() < mutation_rate:
|
||||||
|
offspring1 = swap_mutation(component_points, offspring1)
|
||||||
|
|
||||||
|
new_population.append(offspring1)
|
||||||
|
new_population.append(offspring2)
|
||||||
|
|
||||||
|
pbar.update(1)
|
||||||
|
|
||||||
|
best_individual = population[np.argmin(pop_val)]
|
||||||
|
val, result = cal_individual_val(component_points, component_nozzle, best_individual)
|
||||||
|
print(result)
|
||||||
|
|
||||||
|
plt.plot(best_popval)
|
||||||
|
plt.show()
|
||||||
|
# TODO: 计算实际的PCB整线组装时间
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# warnings.simplefilter('ignore')
|
||||||
|
# 参数解析
|
||||||
|
parser = argparse.ArgumentParser(description='assembly line optimizer implementation')
|
||||||
|
parser.add_argument('--filename', default='PCB.txt', type=str, help='load pcb data')
|
||||||
|
parser.add_argument('--auto_register', default=1, type=int, help='register the component according the pcb data')
|
||||||
|
|
||||||
|
params = parser.parse_args()
|
||||||
|
|
||||||
|
# 结果输出显示所有行和列
|
||||||
|
pd.set_option('display.max_columns', None)
|
||||||
|
pd.set_option('display.max_rows', None)
|
||||||
|
|
||||||
|
# 加载PCB数据
|
||||||
|
pcb_data, component_data, _ = load_data(params.filename, component_register=params.auto_register) # 加载PCB数据
|
||||||
|
|
||||||
|
optimizer(pcb_data, component_data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
78
optimizer_common.py
Normal file
78
optimizer_common.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import copy
|
||||||
|
import time
|
||||||
|
import math
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
from collections import defaultdict
|
||||||
|
from typing import List, Counter
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
# 机器参数
|
||||||
|
max_head_index, max_slot_index = 6, 120 # 暂时默认所有机器参数相同
|
||||||
|
max_machine_index = 3
|
||||||
|
interval_ratio = 2
|
||||||
|
slot_interval = 15
|
||||||
|
head_interval = slot_interval * interval_ratio
|
||||||
|
head_nozzle = ['' for _ in range(max_head_index)] # 头上已经分配吸嘴
|
||||||
|
|
||||||
|
# 位置信息
|
||||||
|
slotf1_pos, slotr1_pos = [-31.267, 44.], [807., 810.545] # F1(前基座最左侧)、R1(后基座最右侧)位置
|
||||||
|
fix_camera_pos = [269.531, 694.823] # 固定相机位置
|
||||||
|
anc_marker_pos = [336.457, 626.230] # ANC基准点位置
|
||||||
|
stopper_pos = [635.150, 124.738] # 止档块位置
|
||||||
|
|
||||||
|
# 时间参数
|
||||||
|
T_pp, T_tr, T_nc = 2, 5, 25
|
||||||
|
|
||||||
|
# 电机参数
|
||||||
|
head_rotary_velocity = 8e-5 # 贴装头R轴旋转时间
|
||||||
|
x_max_velocity, y_max_velocity = 1.4, 1.2
|
||||||
|
x_max_acceleration, y_max_acceleration = x_max_velocity / 0.079, y_max_velocity / 0.079
|
||||||
|
|
||||||
|
# 不同种类供料器宽度
|
||||||
|
feeder_width = {'SM8': (7.25, 7.25), 'SM12': (7.00, 20.00), 'SM16': (7.00, 22.00),
|
||||||
|
'SM24': (7.00, 29.00), 'SM32': (7.00, 44.00)}
|
||||||
|
|
||||||
|
# 可用吸嘴数量限制
|
||||||
|
nozzle_limit = {'CN065': 6, 'CN040': 6, 'CN220': 6, 'CN400': 6, 'CN140': 6}
|
||||||
|
|
||||||
|
|
||||||
|
def axis_moving_time(distance, axis=0):
|
||||||
|
distance = abs(distance) * 1e-3
|
||||||
|
Lamax = x_max_velocity ** 2 / x_max_acceleration if axis == 0 else y_max_velocity ** 2 / y_max_acceleration
|
||||||
|
Tmax = x_max_velocity / x_max_acceleration if axis == 0 else y_max_velocity / y_max_acceleration
|
||||||
|
if axis == 0:
|
||||||
|
return 2 * math.sqrt(distance / x_max_acceleration) if distance < Lamax else 2 * Tmax + (
|
||||||
|
distance - Lamax) / x_max_velocity
|
||||||
|
else:
|
||||||
|
return 2 * math.sqrt(distance / y_max_acceleration) if distance < Lamax else 2 * Tmax + (
|
||||||
|
distance - Lamax) / y_max_velocity
|
||||||
|
|
||||||
|
|
||||||
|
def head_rotary_time(angle):
|
||||||
|
while -180 > angle > 180:
|
||||||
|
if angle > 180:
|
||||||
|
angle -= 360
|
||||||
|
else:
|
||||||
|
angle += 360
|
||||||
|
return abs(angle) * head_rotary_velocity
|
||||||
|
|
||||||
|
|
||||||
|
def timer_wrapper(func):
|
||||||
|
@wraps(func)
|
||||||
|
def measure_time(*args, **kwargs):
|
||||||
|
start_time = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
|
||||||
|
print("function {} running time : {} s".format(func.__name__, time.time() - start_time))
|
||||||
|
return result
|
||||||
|
|
||||||
|
return measure_time
|
||||||
|
|
||||||
|
|
579
result_analysis.py
Normal file
579
result_analysis.py
Normal file
@ -0,0 +1,579 @@
|
|||||||
|
from optimizer_common import *
|
||||||
|
|
||||||
|
# 绘制各周期从供料器周期拾取的元件位置
|
||||||
|
def pickup_cycle_schematic(feeder_slot_result, cycle_result):
|
||||||
|
plt.rcParams['font.sans-serif'] = ['KaiTi'] # 指定默认字体
|
||||||
|
plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
|
||||||
|
# data
|
||||||
|
bar_width = .7
|
||||||
|
feeder_part = np.zeros((int)(max_slot_index / 2), dtype = np.int)
|
||||||
|
for cycle in range(len(feeder_slot_result)):
|
||||||
|
label_str = '周期' + str(cycle + 1)
|
||||||
|
cur_feeder_part = np.zeros((int)(max_slot_index / 2), dtype = np.int)
|
||||||
|
for slot in feeder_slot_result[cycle]:
|
||||||
|
if slot > 0:
|
||||||
|
cur_feeder_part[slot] += cycle_result[cycle]
|
||||||
|
|
||||||
|
plt.bar(np.arange(max_slot_index / 2), cur_feeder_part, bar_width, edgecolor='black', bottom=feeder_part,
|
||||||
|
label=label_str)
|
||||||
|
|
||||||
|
for slot in feeder_slot_result[cycle]:
|
||||||
|
if slot > 0:
|
||||||
|
feeder_part[slot] += cycle_result[cycle]
|
||||||
|
|
||||||
|
plt.legend()
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
def placement_route_schematic(pcb_data, component_result, cycle_result, feeder_slot_result, placement_result,
|
||||||
|
head_sequence, cycle=-1):
|
||||||
|
|
||||||
|
plt.figure('cycle {}'.format(cycle + 1))
|
||||||
|
pos_x, pos_y = [], []
|
||||||
|
for i in range(len(pcb_data)):
|
||||||
|
pos_x.append(pcb_data.loc[i]['x'] + stopper_pos[0])
|
||||||
|
pos_y.append(pcb_data.loc[i]['y'] + stopper_pos[1])
|
||||||
|
# plt.text(pcb_data.loc[i]['x'], pcb_data.loc[i]['y'] + 0.1, '%d' % i, ha='center', va = 'bottom', size = 8)
|
||||||
|
|
||||||
|
mount_pos = []
|
||||||
|
for head in head_sequence[cycle]:
|
||||||
|
index = placement_result[cycle][head]
|
||||||
|
plt.text(pos_x[index], pos_y[index] + 0.1, 'HD%d' % (head + 1), ha='center', va = 'bottom', size = 10)
|
||||||
|
plt.plot([pos_x[index], pos_x[index] - head * head_interval], [pos_y[index], pos_y[index]], linestyle='-.',
|
||||||
|
color='black', linewidth=1)
|
||||||
|
mount_pos.append([pos_x[index] - head * head_interval, pos_y[index]])
|
||||||
|
plt.plot(mount_pos[-1][0], mount_pos[-1][1], marker='^', color='red', markerfacecolor='white')
|
||||||
|
|
||||||
|
# plt.text(mount_pos[-1][0], mount_pos[-1][1], '%d' % index, size=8)
|
||||||
|
|
||||||
|
# 绘制贴装路径
|
||||||
|
for i in range(len(mount_pos) - 1):
|
||||||
|
plt.plot([mount_pos[i][0], mount_pos[i + 1][0]], [mount_pos[i][1], mount_pos[i + 1][1]], color='blue', linewidth=1)
|
||||||
|
|
||||||
|
draw_x, draw_y = [], []
|
||||||
|
for c in range(cycle, len(placement_result)):
|
||||||
|
for h in range(max_head_index):
|
||||||
|
i = placement_result[c][h]
|
||||||
|
if i == -1:
|
||||||
|
continue
|
||||||
|
draw_x.append(pcb_data.loc[i]['x'] + stopper_pos[0])
|
||||||
|
draw_y.append(pcb_data.loc[i]['y'] + stopper_pos[1])
|
||||||
|
|
||||||
|
# plt.text(draw_x[-1], draw_y[-1] - 5, '%d' % i, ha='center', va='bottom', size=10)
|
||||||
|
|
||||||
|
plt.scatter(draw_x, draw_y, s=8)
|
||||||
|
|
||||||
|
# 绘制供料器位置布局
|
||||||
|
for slot in range(max_slot_index // 2):
|
||||||
|
plt.scatter(slotf1_pos[0] + slot_interval * slot, slotf1_pos[1], marker = 'x', s = 12, color = 'green')
|
||||||
|
plt.text(slotf1_pos[0] + slot_interval * slot, slotf1_pos[1] - 50, slot + 1, ha = 'center', va = 'bottom', size = 8)
|
||||||
|
|
||||||
|
feeder_part, feeder_counter = {}, {}
|
||||||
|
placement_cycle = 0
|
||||||
|
for cycle_, components in enumerate(component_result):
|
||||||
|
for head, component in enumerate(components):
|
||||||
|
if component == -1:
|
||||||
|
continue
|
||||||
|
placement = placement_result[placement_cycle][head]
|
||||||
|
slot = feeder_slot_result[cycle_][head]
|
||||||
|
feeder_part[slot] = pcb_data.loc[placement]['part']
|
||||||
|
if slot not in feeder_counter.keys():
|
||||||
|
feeder_counter[slot] = 0
|
||||||
|
|
||||||
|
feeder_counter[slot] += cycle_result[cycle_]
|
||||||
|
placement_cycle += cycle_result[cycle_]
|
||||||
|
|
||||||
|
for slot, part in feeder_part.items():
|
||||||
|
plt.text(slotf1_pos[0] + slot_interval * (slot - 1), slotf1_pos[1] + 15, part + ': ' + str(feeder_counter[slot]), ha = 'center', size = 7, rotation = 90)
|
||||||
|
|
||||||
|
plt.plot([slotf1_pos[0] - slot_interval / 2, slotf1_pos[0] + slot_interval * (max_slot_index // 2 - 1 + 0.5)],
|
||||||
|
[slotf1_pos[1] + 10, slotf1_pos[1] + 10], color = 'black')
|
||||||
|
plt.plot([slotf1_pos[0] - slot_interval / 2, slotf1_pos[0] + slot_interval * (max_slot_index // 2 - 1 + 0.5)],
|
||||||
|
[slotf1_pos[1] - 40, slotf1_pos[1] - 40], color = 'black')
|
||||||
|
|
||||||
|
for counter in range(max_slot_index // 2 + 1):
|
||||||
|
pos = slotf1_pos[0] + (counter - 0.5) * slot_interval
|
||||||
|
plt.plot([pos, pos], [slotf1_pos[1] + 10, slotf1_pos[1] - 40], color='black', linewidth = 1)
|
||||||
|
|
||||||
|
# 绘制拾取路径
|
||||||
|
pick_slot = []
|
||||||
|
cycle_group = 0
|
||||||
|
while sum(cycle_result[0: cycle_group + 1]) < cycle:
|
||||||
|
cycle_group += 1
|
||||||
|
for head, slot in enumerate(feeder_slot_result[cycle_group]):
|
||||||
|
if slot == -1:
|
||||||
|
continue
|
||||||
|
pick_slot.append(slot - head * interval_ratio)
|
||||||
|
pick_slot = list(set(pick_slot))
|
||||||
|
pick_slot = sorted(pick_slot)
|
||||||
|
|
||||||
|
next_cycle_group = 0
|
||||||
|
next_pick_slot = max_slot_index
|
||||||
|
while sum(cycle_result[0: next_cycle_group + 1]) < cycle + 1:
|
||||||
|
next_cycle_group += 1
|
||||||
|
if next_cycle_group < len(feeder_slot_result):
|
||||||
|
for head, slot in enumerate(feeder_slot_result[cycle_group]):
|
||||||
|
if slot == -1:
|
||||||
|
continue
|
||||||
|
next_pick_slot = min(next_pick_slot, slot - head * interval_ratio)
|
||||||
|
|
||||||
|
# 前往PCB贴装
|
||||||
|
plt.plot([mount_pos[-1][0], slotf1_pos[0] + slot_interval * (pick_slot[-1] - 1)], [mount_pos[-1][1], slotf1_pos[1]],
|
||||||
|
color='blue', linewidth=1)
|
||||||
|
# 基座移动路径
|
||||||
|
plt.plot([slotf1_pos[0] + slot_interval * (pick_slot[0] - 1), slotf1_pos[0] + slot_interval * (pick_slot[-1] - 1)],
|
||||||
|
[slotf1_pos[1], slotf1_pos[1]], color='blue', linewidth=1)
|
||||||
|
# 返回基座取料
|
||||||
|
plt.plot([mount_pos[0][0], slotf1_pos[0] + slot_interval * (next_pick_slot - 1)], [mount_pos[0][1], slotf1_pos[1]],
|
||||||
|
color='blue', linewidth=1)
|
||||||
|
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
def save_placement_route_figure(file_name, pcb_data, component_result, cycle_result, feeder_slot_result, placement_result, head_sequence):
|
||||||
|
path = 'result/' + file_name[:file_name.find('.')]
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.mkdir(path)
|
||||||
|
|
||||||
|
pos_x, pos_y = [], []
|
||||||
|
for i in range(len(pcb_data)):
|
||||||
|
pos_x.append(pcb_data.loc[i]['x'] + stopper_pos[0])
|
||||||
|
pos_y.append(pcb_data.loc[i]['y'] + stopper_pos[1])
|
||||||
|
# plt.text(pcb_data.loc[i]['x'], pcb_data.loc[i]['y'] + 0.1, '%d' % i, ha='center', va = 'bottom', size = 8)
|
||||||
|
|
||||||
|
with tqdm(total=100) as pbar:
|
||||||
|
pbar.set_description('save figure')
|
||||||
|
for cycle in range(len(placement_result)):
|
||||||
|
plt.figure(cycle)
|
||||||
|
|
||||||
|
mount_pos = []
|
||||||
|
for head in head_sequence[cycle]:
|
||||||
|
index = placement_result[cycle][head]
|
||||||
|
plt.text(pos_x[index], pos_y[index] + 0.1, 'HD%d' % (head + 1), ha='center', va='bottom', size=10)
|
||||||
|
plt.plot([pos_x[index], pos_x[index] - head * head_interval], [pos_y[index], pos_y[index]], linestyle='-.',
|
||||||
|
color='black', linewidth=1)
|
||||||
|
mount_pos.append([pos_x[index] - head * head_interval, pos_y[index]])
|
||||||
|
plt.plot(mount_pos[-1][0], mount_pos[-1][1], marker='^', color='red', markerfacecolor='white')
|
||||||
|
|
||||||
|
# 绘制贴装路径
|
||||||
|
for i in range(len(mount_pos) - 1):
|
||||||
|
plt.plot([mount_pos[i][0], mount_pos[i + 1][0]], [mount_pos[i][1], mount_pos[i + 1][1]], color='blue',
|
||||||
|
linewidth=1)
|
||||||
|
|
||||||
|
draw_x, draw_y = [], []
|
||||||
|
for c in range(cycle, len(placement_result)):
|
||||||
|
for h in range(max_head_index):
|
||||||
|
i = placement_result[c][h]
|
||||||
|
if i == -1:
|
||||||
|
continue
|
||||||
|
draw_x.append(pcb_data.loc[i]['x'] + stopper_pos[0])
|
||||||
|
draw_y.append(pcb_data.loc[i]['y'] + stopper_pos[1])
|
||||||
|
|
||||||
|
# plt.text(draw_x[-1], draw_y[-1] - 5, '%d' % i, ha='center', va='bottom', size=10)
|
||||||
|
|
||||||
|
plt.scatter(pos_x, pos_y, s=8)
|
||||||
|
# 绘制供料器位置布局
|
||||||
|
for slot in range(max_slot_index // 2):
|
||||||
|
plt.scatter(slotf1_pos[0] + slot_interval * slot, slotf1_pos[1], marker='x', s=12, color='green')
|
||||||
|
plt.text(slotf1_pos[0] + slot_interval * slot, slotf1_pos[1] - 50, slot + 1, ha='center', va='bottom', size=8)
|
||||||
|
|
||||||
|
feeder_part, feeder_counter = {}, {}
|
||||||
|
placement_cycle = 0
|
||||||
|
for cycle_, components in enumerate(component_result):
|
||||||
|
for head, component in enumerate(components):
|
||||||
|
if component == -1:
|
||||||
|
continue
|
||||||
|
placement = placement_result[placement_cycle][head]
|
||||||
|
slot = feeder_slot_result[cycle_][head]
|
||||||
|
feeder_part[slot] = pcb_data.loc[placement]['part']
|
||||||
|
if slot not in feeder_counter.keys():
|
||||||
|
feeder_counter[slot] = 0
|
||||||
|
|
||||||
|
feeder_counter[slot] += cycle_result[cycle_]
|
||||||
|
placement_cycle += cycle_result[cycle_]
|
||||||
|
|
||||||
|
for slot, part in feeder_part.items():
|
||||||
|
plt.text(slotf1_pos[0] + slot_interval * (slot - 1), slotf1_pos[1] + 15,
|
||||||
|
part + ': ' + str(feeder_counter[slot]), ha='center', size=7, rotation=90)
|
||||||
|
|
||||||
|
plt.plot([slotf1_pos[0] - slot_interval / 2, slotf1_pos[0] + slot_interval * (max_slot_index // 2 - 1 + 0.5)],
|
||||||
|
[slotf1_pos[1] + 10, slotf1_pos[1] + 10], color='black')
|
||||||
|
plt.plot([slotf1_pos[0] - slot_interval / 2, slotf1_pos[0] + slot_interval * (max_slot_index // 2 - 1 + 0.5)],
|
||||||
|
[slotf1_pos[1] - 40, slotf1_pos[1] - 40], color='black')
|
||||||
|
|
||||||
|
for counter in range(max_slot_index // 2 + 1):
|
||||||
|
pos = slotf1_pos[0] + (counter - 0.5) * slot_interval
|
||||||
|
plt.plot([pos, pos], [slotf1_pos[1] + 10, slotf1_pos[1] - 40], color='black', linewidth=1)
|
||||||
|
|
||||||
|
# 绘制拾取路径
|
||||||
|
pick_slot = []
|
||||||
|
cycle_group = 0
|
||||||
|
while sum(cycle_result[0: cycle_group + 1]) < cycle:
|
||||||
|
cycle_group += 1
|
||||||
|
for head, slot in enumerate(feeder_slot_result[cycle_group]):
|
||||||
|
if slot == -1:
|
||||||
|
continue
|
||||||
|
pick_slot.append(slot - head * interval_ratio)
|
||||||
|
pick_slot = list(set(pick_slot))
|
||||||
|
pick_slot = sorted(pick_slot)
|
||||||
|
|
||||||
|
plt.plot([mount_pos[0][0], slotf1_pos[0] + slot_interval * (pick_slot[0] - 1)], [mount_pos[0][1], slotf1_pos[1]],
|
||||||
|
color='blue', linewidth=1)
|
||||||
|
plt.plot([mount_pos[-1][0], slotf1_pos[0] + slot_interval * (pick_slot[-1] - 1)], [mount_pos[-1][1], slotf1_pos[1]],
|
||||||
|
color='blue', linewidth=1)
|
||||||
|
plt.plot([slotf1_pos[0] + slot_interval * (pick_slot[0] - 1), slotf1_pos[0] + slot_interval * (pick_slot[-1] - 1)],
|
||||||
|
[slotf1_pos[1], slotf1_pos[1]], color='blue', linewidth=1)
|
||||||
|
|
||||||
|
plt.savefig(path + '/cycle_{}'.format(cycle + 1))
|
||||||
|
|
||||||
|
plt.close(cycle)
|
||||||
|
pbar.update(100 / len(placement_result))
|
||||||
|
|
||||||
|
|
||||||
|
def output_optimize_result(file_name, method, component_data, pcb_data, feeder_data, component_result, cycle_result,
|
||||||
|
feeder_slot_result, placement_result, head_sequence):
|
||||||
|
assert len(component_result) == len(feeder_slot_result)
|
||||||
|
if feeder_data is None:
|
||||||
|
warning_info = 'file: ' + file_name + ' optimize result is not existed!'
|
||||||
|
warnings.warn(warning_info, UserWarning)
|
||||||
|
return
|
||||||
|
|
||||||
|
output_data = pcb_data.copy(deep=True)
|
||||||
|
|
||||||
|
# 默认ANC参数
|
||||||
|
anc_list = defaultdict(list)
|
||||||
|
anc_list['CN065'] = list(range(14, 25, 2))
|
||||||
|
anc_list['CN220'] = list(range(15, 26, 2))
|
||||||
|
anc_list['CN140'] = list(range(26, 37, 2))
|
||||||
|
anc_list['CN400'] = list(range(27, 38, 2))
|
||||||
|
|
||||||
|
# 更新供料器组参数
|
||||||
|
for cycle_set in range(len(cycle_result)):
|
||||||
|
for head, component in enumerate(component_result[cycle_set]):
|
||||||
|
if component == -1:
|
||||||
|
continue
|
||||||
|
if feeder_data[feeder_data['slot'] == feeder_slot_result[cycle_set][head]].index.empty:
|
||||||
|
part = component_data.loc[component]['part']
|
||||||
|
feeder_data.loc[len(feeder_data.index)] = [feeder_slot_result[cycle_set][head], part, 0]
|
||||||
|
feeder_data.sort_values('slot', inplace=True, ascending=True, ignore_index=True)
|
||||||
|
|
||||||
|
placement_index = []
|
||||||
|
assigned_nozzle, assigned_anc_hole = ['' for _ in range(max_head_index)], [-1 for _ in range(max_head_index)]
|
||||||
|
for cycle_set in range(len(cycle_result)):
|
||||||
|
floor_cycle, ceil_cycle = sum(cycle_result[:cycle_set]), sum(cycle_result[:(cycle_set + 1)])
|
||||||
|
for cycle in range(floor_cycle, ceil_cycle):
|
||||||
|
cycle_start = True
|
||||||
|
cycle_nozzle = ['' for _ in range(max_head_index)]
|
||||||
|
head_indexes = [-1 for _ in range(max_head_index)]
|
||||||
|
for head in head_sequence[cycle]:
|
||||||
|
index_ = placement_result[cycle][head]
|
||||||
|
if index_ == -1:
|
||||||
|
continue
|
||||||
|
head_indexes[head] = index_
|
||||||
|
placement_index.append(index_)
|
||||||
|
|
||||||
|
output_data.loc[index_, 'cs'] = 1 if cycle_start else 0
|
||||||
|
output_data.loc[index_, 'cy'] = cycle + 1
|
||||||
|
output_data.loc[index_, 'hd'] = head + 1
|
||||||
|
|
||||||
|
cycle_start = False
|
||||||
|
|
||||||
|
# 供料器信息
|
||||||
|
slot = feeder_slot_result[cycle_set][head]
|
||||||
|
fdr = 'F' + str(slot) if slot < max_slot_index // 2 else 'R' + str(slot - max_slot_index // 2)
|
||||||
|
feeder_index = feeder_data[feeder_data['slot'] == slot].index.tolist()[0]
|
||||||
|
|
||||||
|
output_data.loc[index_, 'fdr'] = fdr + ' ' + feeder_data.loc[feeder_index, 'part']
|
||||||
|
|
||||||
|
# ANC信息
|
||||||
|
cycle_nozzle[head] = component_data.loc[component_result[cycle_set][head], 'nz']
|
||||||
|
|
||||||
|
for head in range(max_head_index):
|
||||||
|
nozzle = cycle_nozzle[head]
|
||||||
|
if nozzle == '':
|
||||||
|
continue
|
||||||
|
if nozzle != assigned_nozzle[head]:
|
||||||
|
# 已分配有吸嘴,卸载原吸嘴
|
||||||
|
if assigned_nozzle[head] != '':
|
||||||
|
anc_list[assigned_nozzle[head]].append(assigned_anc_hole[head])
|
||||||
|
anc_list[assigned_nozzle[head]] = sorted(anc_list[assigned_nozzle[head]])
|
||||||
|
|
||||||
|
# 安装新的吸嘴
|
||||||
|
assigned_nozzle[head] = nozzle
|
||||||
|
try:
|
||||||
|
assigned_anc_hole[head] = anc_list[nozzle][0]
|
||||||
|
except IndexError:
|
||||||
|
info = 'the number of nozzle for [' + nozzle + '] exceeds the quantity limit'
|
||||||
|
raise IndexError(info)
|
||||||
|
anc_list[nozzle].pop(0)
|
||||||
|
|
||||||
|
output_data.loc[head_indexes[head], 'nz'] = '1-' + str(assigned_anc_hole[head]) + ' ' + nozzle
|
||||||
|
|
||||||
|
output_data = output_data.reindex(placement_index)
|
||||||
|
output_data = output_data.reset_index(drop=True)
|
||||||
|
if 'desc' not in output_data.columns:
|
||||||
|
column_index = int(np.where(output_data.columns.values.reshape(-1) == 'part')[0][0])
|
||||||
|
output_data.insert(loc=column_index + 1, column='desc', value='')
|
||||||
|
|
||||||
|
if not os.path.exists('result/' + method):
|
||||||
|
os.makedirs('result/' + method)
|
||||||
|
|
||||||
|
file_name = method + '/' + file_name.split('.')[0] + '.xlsx'
|
||||||
|
output_data.to_excel('result/' + file_name, sheet_name='tb1', float_format='%.3f', na_rep='')
|
||||||
|
|
||||||
|
|
||||||
|
def component_assign_evaluate(component_data, component_result, cycle_result, feeder_slot_result) -> float:
|
||||||
|
nozzle_change_counter = 0
|
||||||
|
for head in range(max_head_index):
|
||||||
|
nozzle = ''
|
||||||
|
for cycle in range(len(component_result)):
|
||||||
|
component_index = component_result[cycle][head]
|
||||||
|
if component_index == -1:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if cycle != 0 and nozzle != component_data.loc[component_index, 'nz']:
|
||||||
|
nozzle_change_counter += 1
|
||||||
|
nozzle = component_data.loc[component_index, 'nz']
|
||||||
|
|
||||||
|
gang_pick_counter = 0
|
||||||
|
for cycle, feeder_slot in enumerate(feeder_slot_result):
|
||||||
|
pick_slot = defaultdict(int)
|
||||||
|
for head, slot in enumerate(feeder_slot):
|
||||||
|
if slot == -1:
|
||||||
|
continue
|
||||||
|
pick_slot[slot - head * interval_ratio] += 1
|
||||||
|
for _ in pick_slot.values():
|
||||||
|
gang_pick_counter += cycle_result[cycle]
|
||||||
|
|
||||||
|
return sum(cycle_result) + e_nz_change * nozzle_change_counter + e_gang_pick * gang_pick_counter
|
||||||
|
|
||||||
|
|
||||||
|
def optimization_assign_result(component_data, pcb_data, component_result, cycle_result, feeder_slot_result,
|
||||||
|
nozzle_hinter=False, component_hinter=False, feeder_hinter=False):
|
||||||
|
if nozzle_hinter:
|
||||||
|
columns = ['H{}'.format(i + 1) for i in range(max_head_index)] + ['cycle']
|
||||||
|
|
||||||
|
nozzle_assign = pd.DataFrame(columns=columns)
|
||||||
|
for cycle, components in enumerate(component_result):
|
||||||
|
nozzle_assign.loc[cycle, 'cycle'] = cycle_result[cycle]
|
||||||
|
for head in range(max_head_index):
|
||||||
|
index = component_result[cycle][head]
|
||||||
|
if index == -1:
|
||||||
|
nozzle_assign.loc[cycle, 'H{}'.format(head + 1)] = ''
|
||||||
|
else:
|
||||||
|
nozzle = component_data.loc[index]['nz']
|
||||||
|
nozzle_assign.loc[cycle, 'H{}'.format(head + 1)] = nozzle
|
||||||
|
|
||||||
|
print(nozzle_assign)
|
||||||
|
print('')
|
||||||
|
|
||||||
|
if component_hinter:
|
||||||
|
columns = ['H{}'.format(i + 1) for i in range(max_head_index)] + ['cycle']
|
||||||
|
|
||||||
|
component_assign = pd.DataFrame(columns=columns)
|
||||||
|
for cycle, components in enumerate(component_result):
|
||||||
|
component_assign.loc[cycle, 'cycle'] = cycle_result[cycle]
|
||||||
|
for head in range(max_head_index):
|
||||||
|
index = component_result[cycle][head]
|
||||||
|
if index == -1:
|
||||||
|
component_assign.loc[cycle, 'H{}'.format(head + 1)] = ''
|
||||||
|
else:
|
||||||
|
part = component_data.loc[index]['part']
|
||||||
|
component_assign.loc[cycle, 'H{}'.format(head + 1)] = part
|
||||||
|
|
||||||
|
print(component_assign)
|
||||||
|
print('')
|
||||||
|
|
||||||
|
if feeder_hinter:
|
||||||
|
columns = ['H{}'.format(i + 1) for i in range(max_head_index)] + ['cycle']
|
||||||
|
|
||||||
|
feedr_assign = pd.DataFrame(columns=columns)
|
||||||
|
for cycle, components in enumerate(feeder_slot_result):
|
||||||
|
feedr_assign.loc[cycle, 'cycle'] = cycle_result[cycle]
|
||||||
|
for head in range(max_head_index):
|
||||||
|
slot = feeder_slot_result[cycle][head]
|
||||||
|
if slot == -1:
|
||||||
|
feedr_assign.loc[cycle, 'H{}'.format(head + 1)] = 'A'
|
||||||
|
else:
|
||||||
|
feedr_assign.loc[cycle, 'H{}'.format(head + 1)] = 'F{}'.format(
|
||||||
|
slot) if slot <= max_slot_index // 2 else 'R{}'.format(slot - max_head_index)
|
||||||
|
|
||||||
|
print(feedr_assign)
|
||||||
|
print('')
|
||||||
|
|
||||||
|
|
||||||
|
def placement_time_estimate(component_data, pcb_data, component_result, cycle_result, feeder_slot_result,
|
||||||
|
placement_result, head_sequence, hinter=True) -> float:
|
||||||
|
# === 校验 ===
|
||||||
|
total_points = 0
|
||||||
|
for cycle, components in enumerate(component_result):
|
||||||
|
for head, component in enumerate(components):
|
||||||
|
if component == -1:
|
||||||
|
continue
|
||||||
|
total_points += cycle_result[cycle]
|
||||||
|
|
||||||
|
if total_points != len(pcb_data):
|
||||||
|
warning_info = 'the number of placement points is not match with the PCB data. '
|
||||||
|
warnings.warn(warning_info, UserWarning)
|
||||||
|
return 0.
|
||||||
|
|
||||||
|
for placements in placement_result:
|
||||||
|
for placement in placements:
|
||||||
|
if placement == -1:
|
||||||
|
continue
|
||||||
|
total_points -= 1
|
||||||
|
|
||||||
|
if total_points != 0:
|
||||||
|
warnings.warn(
|
||||||
|
'the optimization result of component assignment result and placement result are not consistent. ',
|
||||||
|
UserWarning)
|
||||||
|
return 0.
|
||||||
|
|
||||||
|
feeder_arrangement = defaultdict(set)
|
||||||
|
for cycle, feeder_slots in enumerate(feeder_slot_result):
|
||||||
|
for head, slot in enumerate(feeder_slots):
|
||||||
|
if slot == -1:
|
||||||
|
continue
|
||||||
|
feeder_arrangement[component_result[cycle][head]].add(slot)
|
||||||
|
|
||||||
|
for part, data in component_data.iterrows():
|
||||||
|
if part in feeder_arrangement.keys() and data['feeder-limit'] < len(feeder_arrangement[part]):
|
||||||
|
info = 'the number of arranged feeder of [' + data['part'] + '] exceeds the quantity limit'
|
||||||
|
warnings.warn(info, UserWarning)
|
||||||
|
return 0.
|
||||||
|
|
||||||
|
t_pick, t_place = .078, .051 # 贴装/拾取用时
|
||||||
|
t_nozzle_put, t_nozzle_pick = 0.9, 0.75 # 装卸吸嘴用时
|
||||||
|
t_fix_camera_check = 0.12 # 固定相机检测时间
|
||||||
|
|
||||||
|
total_moving_time = .0 # 总移动用时
|
||||||
|
total_operation_time = .0 # 操作用时
|
||||||
|
total_nozzle_change_counter = 0 # 总吸嘴更换次数
|
||||||
|
total_pick_counter = 0 # 总拾取次数
|
||||||
|
total_mount_distance, total_pick_distance = .0, .0 # 贴装距离、拾取距离
|
||||||
|
total_distance = 0 # 总移动距离
|
||||||
|
cur_pos, next_pos = anc_marker_pos, [0, 0] # 贴装头当前位置
|
||||||
|
|
||||||
|
# 初始化首个周期的吸嘴装配信息
|
||||||
|
nozzle_assigned = ['Empty' for _ in range(max_head_index)]
|
||||||
|
for head in range(max_head_index):
|
||||||
|
for cycle in range(len(component_result)):
|
||||||
|
idx = component_result[cycle][head]
|
||||||
|
if idx == -1:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
nozzle_assigned[head] = component_data.loc[idx]['nz']
|
||||||
|
break
|
||||||
|
|
||||||
|
for cycle_set, _ in enumerate(component_result):
|
||||||
|
floor_cycle, ceil_cycle = sum(cycle_result[:cycle_set]), sum(cycle_result[:(cycle_set + 1)])
|
||||||
|
for cycle in range(floor_cycle, ceil_cycle):
|
||||||
|
pick_slot, mount_pos, mount_angle = [], [], []
|
||||||
|
nozzle_pick_counter, nozzle_put_counter = 0, 0 # 吸嘴更换次数统计(拾取/放置分别算一次)
|
||||||
|
for head in range(max_head_index):
|
||||||
|
if feeder_slot_result[cycle_set][head] != -1:
|
||||||
|
pick_slot.append(feeder_slot_result[cycle_set][head] - interval_ratio * head)
|
||||||
|
if component_result[cycle_set][head] == -1:
|
||||||
|
continue
|
||||||
|
nozzle = component_data.loc[component_result[cycle_set][head]]['nz']
|
||||||
|
if nozzle != nozzle_assigned[head]:
|
||||||
|
if nozzle_assigned[head] != 'Empty':
|
||||||
|
nozzle_put_counter += 1
|
||||||
|
nozzle_pick_counter += 1
|
||||||
|
nozzle_assigned[head] = nozzle
|
||||||
|
|
||||||
|
# ANC处进行吸嘴更换
|
||||||
|
if nozzle_pick_counter + nozzle_put_counter > 0:
|
||||||
|
next_pos = anc_marker_pos
|
||||||
|
total_moving_time += max(axis_moving_time(cur_pos[0] - next_pos[0], 0),
|
||||||
|
axis_moving_time(cur_pos[1] - next_pos[1], 1))
|
||||||
|
total_distance += max(abs(cur_pos[0] - next_pos[0]), abs(cur_pos[1] - next_pos[1]))
|
||||||
|
cur_pos = next_pos
|
||||||
|
|
||||||
|
pick_slot = list(set(pick_slot))
|
||||||
|
pick_slot = sorted(pick_slot, reverse=True)
|
||||||
|
|
||||||
|
# 拾取路径(自右向左)
|
||||||
|
for slot in pick_slot:
|
||||||
|
if slot < max_slot_index // 2:
|
||||||
|
next_pos = [slotf1_pos[0] + slot_interval * (slot - 1), slotf1_pos[1]]
|
||||||
|
else:
|
||||||
|
next_pos = [slotr1_pos[0] - slot_interval * (max_slot_index - slot - 1), slotr1_pos[1]]
|
||||||
|
total_operation_time += t_pick
|
||||||
|
total_pick_counter += 1
|
||||||
|
total_moving_time += max(axis_moving_time(cur_pos[0] - next_pos[0], 0),
|
||||||
|
axis_moving_time(cur_pos[1] - next_pos[1], 1))
|
||||||
|
total_distance += max(abs(cur_pos[0] - next_pos[0]), abs(cur_pos[1] - next_pos[1]))
|
||||||
|
if slot != pick_slot[0]:
|
||||||
|
total_pick_distance += max(abs(cur_pos[0] - next_pos[0]), abs(cur_pos[1] - next_pos[1]))
|
||||||
|
cur_pos = next_pos
|
||||||
|
|
||||||
|
# 固定相机检测
|
||||||
|
for head in range(max_head_index):
|
||||||
|
if component_result[cycle_set][head] == -1:
|
||||||
|
continue
|
||||||
|
camera = component_data.loc[component_result[cycle_set][head]]['camera']
|
||||||
|
if camera == '固定相机':
|
||||||
|
next_pos = [fix_camera_pos[0] - head * head_interval, fix_camera_pos[1]]
|
||||||
|
total_moving_time += max(axis_moving_time(cur_pos[0] - next_pos[0], 0),
|
||||||
|
axis_moving_time(cur_pos[1] - next_pos[1], 1))
|
||||||
|
total_distance += max(abs(cur_pos[0] - next_pos[0]), abs(cur_pos[1] - next_pos[1]))
|
||||||
|
total_operation_time += t_fix_camera_check
|
||||||
|
cur_pos = next_pos
|
||||||
|
|
||||||
|
# 贴装路径
|
||||||
|
for head in head_sequence[cycle]:
|
||||||
|
index = placement_result[cycle][head]
|
||||||
|
if index == -1:
|
||||||
|
continue
|
||||||
|
mount_pos.append([pcb_data.loc[index]['x'] - head * head_interval + stopper_pos[0],
|
||||||
|
pcb_data.loc[index]['y'] + stopper_pos[1]])
|
||||||
|
mount_angle.append(pcb_data.loc[index]['r'])
|
||||||
|
|
||||||
|
# 单独计算贴装路径
|
||||||
|
for cntPoints in range(len(mount_pos) - 1):
|
||||||
|
total_mount_distance += max(abs(mount_pos[cntPoints][0] - mount_pos[cntPoints + 1][0]),
|
||||||
|
abs(mount_pos[cntPoints][1] - mount_pos[cntPoints + 1][1]))
|
||||||
|
|
||||||
|
# 考虑R轴预旋转,补偿同轴角度转动带来的额外贴装用时
|
||||||
|
total_operation_time += head_rotary_time(mount_angle[0]) # 补偿角度转动带来的额外贴装用时
|
||||||
|
total_operation_time += t_nozzle_put * nozzle_put_counter + t_nozzle_pick * nozzle_pick_counter
|
||||||
|
for pos in mount_pos:
|
||||||
|
total_operation_time += t_place
|
||||||
|
total_moving_time += max(axis_moving_time(cur_pos[0] - pos[0], 0),
|
||||||
|
axis_moving_time(cur_pos[1] - pos[1], 1))
|
||||||
|
total_distance += max(abs(cur_pos[0] - pos[0]), abs(cur_pos[1] - pos[1]))
|
||||||
|
cur_pos = pos
|
||||||
|
|
||||||
|
total_nozzle_change_counter += nozzle_put_counter + nozzle_pick_counter
|
||||||
|
|
||||||
|
total_time = total_moving_time + total_operation_time
|
||||||
|
minutes, seconds = int(total_time // 60), int(total_time) % 60
|
||||||
|
millisecond = int((total_time - minutes * 60 - seconds) * 60)
|
||||||
|
|
||||||
|
if hinter:
|
||||||
|
optimization_assign_result(component_data, pcb_data, component_result, cycle_result, feeder_slot_result,
|
||||||
|
nozzle_hinter=True, component_hinter=True, feeder_hinter=True)
|
||||||
|
|
||||||
|
print('-Cycle counter: {}'.format(sum(cycle_result)))
|
||||||
|
print('-Nozzle change counter: {}'.format(total_nozzle_change_counter // 2))
|
||||||
|
print('-Pick operation counter: {}'.format(total_pick_counter))
|
||||||
|
|
||||||
|
print('-Expected mounting tour length: {} mm'.format(total_mount_distance))
|
||||||
|
print('-Expected picking tour length: {} mm'.format(total_pick_distance))
|
||||||
|
print('-Expected total tour length: {} mm'.format(total_distance))
|
||||||
|
|
||||||
|
print('-Expected total moving time: {} s'.format(total_moving_time))
|
||||||
|
print('-Expected total operation time: {} s'.format(total_operation_time))
|
||||||
|
|
||||||
|
if minutes > 0:
|
||||||
|
print('-Mounting time estimation: {:d} min {} s {:2d} ms ({:.3f}s)'.format(minutes, seconds, millisecond,
|
||||||
|
total_time))
|
||||||
|
else:
|
||||||
|
print('-Mounting time estimation: {} s {:2d} ms ({:.3f}s)'.format(seconds, millisecond, total_time))
|
||||||
|
|
||||||
|
return total_time
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user