增加了HSMO整线优化方法,读取数据增加了供料器部分

This commit is contained in:
2025-08-10 16:58:42 +08:00
parent 045f2f394d
commit 4fd5560650
17 changed files with 1765 additions and 352 deletions

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@ Scripts/
*.pkl
*.pth
*.m
opt/

View File

@@ -22,6 +22,7 @@ import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
import traceback
import openpyxl
matplotlib.use('TkAgg')
@@ -67,7 +68,16 @@ t_fix_camera_check = 0.12 # 固定相机检测时间
T_pp, T_tr, T_nc, T_pl = 2, 5, 25, 0
# 时间参数 (数据拟合获得)
Fit_cy, Fit_nz, Fit_pu, Fit_pl, Fit_mv = 0.326, 0.870, 0.159, 0.041, 0.001
# Fit_cy, Fit_nz, Fit_pu, Fit_pl, Fit_mv = 0.326, 0.870, 0.159, 0.041, 0.030
Fit_cy, Fit_nz, Fit_pu, Fit_pl, Fit_mv = 0.326, 0.870, 0.159, 0.041, 0.035
class Point:
def __init__(self, _x, _y, _r=0, _h=None):
self.x = _x
self.y = _y
self.r = _r
self.h = _h
class OptResult:
@@ -108,9 +118,12 @@ class OptInfo:
print(f'-Pick time: {self.pickup_time: .3f}, Pick distance: {self.pickup_distance: .3f}')
print(f'-Place time: {self.place_time: .3f}, Place distance: {self.place_distance: .3f}')
print(
f'-Round time: {self.total_time - self.place_time - self.place_time: .3f}, Round distance: '
f'-Round time: {self.total_time - self.operation_time - self.pickup_time - self.place_time: .3f}, Round distance: '
f'{self.total_distance - self.pickup_distance - self.place_distance: .3f}')
print(f'-Round & place time per cycle: {(self.total_time - self.pickup_time - self.operation_time) * 1000.0 / (self.cycle_counter + 1e-10): .3f}, ', end='')
print(f'-Round & place distance per cycle: {(self.total_distance - self.pickup_distance) / (self.cycle_counter + 1e-10): .3f}')
minutes, seconds = int(self.total_time // 60), int(self.total_time) % 60
millisecond = int((self.total_time - minutes * 60 - seconds) * 60)
@@ -856,3 +869,10 @@ def random_division(num, div):
def list_range(start, end=None):
return list(range(start)) if end is None else list(range(start, end))
def kth_indices_partition(num, kth):
if len(num) > kth:
return np.argpartition(num, kth)
else:
return np.array(range(len(num)))

View File

@@ -15,26 +15,22 @@ def base_optimizer(machine_index, pcb_data, component_data, feeder_data, params,
if params.machine_optimizer == 'cell-division': # 基于元胞分裂的遗传算法
component_result, cycle_result, feeder_slot_result = optimizer_celldivision(pcb_data, component_data)
placement_result, head_sequence = greedy_placement_route_generation(component_data, pcb_data, component_result,
cycle_result, feeder_slot_result)
placement_result, head_sequence = place_allocate_sequence_route_generation(component_data, pcb_data,
component_result, cycle_result,
feeder_slot_result)
elif params.machine_optimizer == 'feeder-priority': # 基于基座扫描的供料器优先算法
component_result, cycle_result, feeder_slot_result = feeder_priority_assignment(component_data, pcb_data,
feeder_data)
placement_result, head_sequence = greedy_placement_route_generation(component_data, pcb_data, component_result,
cycle_result, feeder_slot_result)
# placement_result, head_sequence = beam_search_route_generation(component_data, pcb_data, component_result,
# cycle_result, feeder_slot_result)
# placement_result, head_sequence = scan_based_placement_route_generation(component_data, pcb_data,
# component_result, cycle_result,
# feeder_slot_result)
placement_result, head_sequence = scan_based_placement_route_generation(component_data, pcb_data,
component_result, cycle_result,
feeder_slot_result)
elif params.machine_optimizer == 'hybrid-genetic': # 基于拾取组的混合遗传算法
component_result, cycle_result, feeder_slot_result, placement_result, head_sequence = optimizer_hybrid_genetic(
pcb_data, component_data, hinter=hinter)
elif params.machine_optimizer == 'aggregation': # 基于batch-level的整数规划 + 启发式算法
component_result, cycle_result, feeder_slot_result, placement_result, head_sequence = optimizer_aggregation(
component_data, pcb_data)
component_data, pcb_data, hinter=hinter)
elif params.machine_optimizer == 'genetic-scanning':
component_result, cycle_result, feeder_slot_result, placement_result, head_sequence = optimizer_genetic_scanning(
component_data, pcb_data, hinter=hinter)
@@ -43,15 +39,16 @@ def base_optimizer(machine_index, pcb_data, component_data, feeder_data, params,
component_data, pcb_data, hinter=hinter)
elif params.machine_optimizer == "two-phase":
component_result, feeder_slot_result, cycle_result = gurobi_optimizer(pcb_data, component_data, feeder_data,
initial=True, partition=True,
initial=True, partition=False,
reduction=True, hinter=hinter)
placement_result, head_sequence = greedy_placement_route_generation(component_data, pcb_data, component_result,
cycle_result, feeder_slot_result)
placement_result, head_sequence = place_allocate_sequence_route_generation(component_data, pcb_data,
component_result, cycle_result,
feeder_slot_result)
else:
raise 'machine optimizer method ' + params.method + ' is not existed'
print('----- Placement machine ' + str(machine_index) + ' ----- ')
# print('----- Placement machine ' + str(machine_index) + ' ----- ')
opt_res = OptResult(component_result, cycle_result, feeder_slot_result, placement_result, head_sequence)
# 估算贴装用时
info = placement_info_evaluation(component_data, pcb_data, opt_res, hinter=False)
@@ -67,4 +64,6 @@ def base_optimizer(machine_index, pcb_data, component_data, feeder_data, params,
f'result/{params.filename[:-4]}-{params.line_optimizer}-M0{machine_index} {params.save_suffix}',
component_data, pcb_data, opt_res)
# output_optimize_result(f'{params.filename[:-4]}', component_data, pcb_data, opt_res)
return info

View File

@@ -355,15 +355,12 @@ def output_optimize_result(file_path, component_data, pcb_data, optimizer_result
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='')
file_dir = file_path[:file_path.rfind('/') + 1]
if not os.path.exists(file_dir):
os.makedirs(file_dir)
output_data.to_excel(file_path + '.xlsx', sheet_name='tb1', float_format='%.3f', na_rep='')
output_data.to_csv('result/' + file_path + '.txt', sep='\t', float_format='%.3f', header=False, index=False)
def optimization_assign_result(component_data, pcb_data, optimizer_result, nozzle_hinter=False, component_hinter=False,
feeder_hinter=False):
feeder_hinter=False, placement_hinter=False):
if nozzle_hinter:
columns = ['H{}'.format(i + 1) for i in range(max_head_index)] + ['cycle']
@@ -428,6 +425,29 @@ def optimization_assign_result(component_data, pcb_data, optimizer_result, nozzl
print(feedr_assign)
print('')
if placement_hinter:
columns = ['H{}'.format(i + 1) for i in range(max_head_index)] + ['cycle']
placement_assign = pd.DataFrame(columns=columns)
for cycle, _ in enumerate(optimizer_result.placement_assign):
placement_assign.loc[cycle, 'cycle'] = 1
for head in range(max_head_index):
point = optimizer_result.placement_assign[cycle][head]
if point != -1:
placement_assign.loc[cycle, 'H{}'.format(head + 1)] = 'P{}'.format(point)
else:
placement_assign.loc[cycle, 'H{}'.format(head + 1)] = ''
headseq_assign = pd.DataFrame(columns=columns)
for cycle, headseq in enumerate(optimizer_result.head_sequence):
headseq_assign.loc[cycle, 'cycle'] = 1
for head in range(len(headseq)):
headseq_assign.loc[cycle, 'H{}'.format(head + 1)] = 'H{}'.format(headseq[head])
print(placement_assign)
print(headseq_assign)
print('')
def placement_info_evaluation(component_data, pcb_data, optimizer_result, hinter=False):
# === 优化结果参数 ===
@@ -555,28 +575,43 @@ def placement_info_evaluation(component_data, pcb_data, optimizer_result, hinter
# 贴装路径
if optimizer_result.placement_assign and optimizer_result.head_sequence:
head_angle = [0 for _ in range(max_head_index)]
for head in optimizer_result.head_sequence[cycle]:
index = optimizer_result.placement_assign[cycle][head]
if index == -1:
continue
mount_pos.append([pcb_data.iloc[index]['x'] - head * head_interval + stopper_pos[0],
pcb_data.iloc[index]['y'] + stopper_pos[1]])
mount_angle.append(pcb_data.iloc[index]['r'])
head_angle[head] = pcb_data.iloc[index]['r']
# 单独计算贴装路径
for cntPoints in range(len(mount_pos) - 1):
info.place_distance += max(abs(mount_pos[cntPoints][0] - mount_pos[cntPoints + 1][0]),
abs(mount_pos[cntPoints][1] - mount_pos[cntPoints + 1][1]))
if mount_pos[0][0] < mount_pos[-1][0]:
mount_pos = reversed(mount_pos)
# 考虑R轴预旋转补偿同轴角度转动带来的额外贴装用时
info.operation_time += head_rotary_time(mount_angle[0]) # 补偿角度转动带来的额外贴装用时
info.operation_time += t_nozzle_put * nozzle_put_counter + t_nozzle_pick * nozzle_pick_counter
for idx, pos in enumerate(mount_pos):
info.operation_time += t_place
move_time = max(axis_moving_time(cur_pos[0] - pos[0], 0), axis_moving_time(cur_pos[1] - pos[1], 1))
if idx == 0:
move_time = max(axis_moving_time(cur_pos[0] - pos[0], 0),
axis_moving_time(cur_pos[1] - pos[1], 1))
info.round_time += move_time
else:
cur_head = optimizer_result.head_sequence[cycle][idx]
side_head = cur_head - 1 if cur_head % 2 else cur_head + 1
if optimizer_result.head_sequence[cycle][idx - 1] != side_head:
move_time = max(axis_moving_time(cur_pos[0] - pos[0], 0),
axis_moving_time(cur_pos[1] - pos[1], 1))
else:
move_time = max(axis_moving_time(cur_pos[0] - pos[0], 0),
axis_moving_time(cur_pos[1] - pos[1], 1),
head_rotary_time(head_angle[cur_head] - head_angle[side_head]))
info.place_time += move_time
info.total_distance += max(abs(cur_pos[0] - pos[0]), abs(cur_pos[1] - pos[1]))
@@ -594,5 +629,3 @@ def placement_info_evaluation(component_data, pcb_data, optimizer_result, hinter
return info

View File

@@ -1,12 +1,12 @@
from base_optimizer.optimizer_common import *
from base_optimizer.smtopt_route import *
def list_range(start, end=None):
return list(range(start)) if end is None else list(range(start, end))
@timer_wrapper
def optimizer_aggregation(component_data, pcb_data):
def optimizer_aggregation(component_data, pcb_data, hinter=True):
# === phase 0: data preparation ===
M = 1000 # a sufficient large number
a, b = 1, 6 # coefficient
@@ -43,7 +43,7 @@ def optimizer_aggregation(component_data, pcb_data):
# === phase 1: mathematical model solver ===
mdl = Model('SMT')
mdl.setParam('OutputFlag', 0)
mdl.setParam('OutputFlag', hinter)
# === Decision Variables ===
# the number of components of type i that are placed by nozzle type j on placement head k
@@ -104,8 +104,7 @@ def optimizer_aggregation(component_data, pcb_data):
mdl.setParam("TimeLimit", 100)
mdl.optimize()
if mdl.Status == GRB.OPTIMAL:
if mdl.Status == GRB.OPTIMAL or mdl.Status == GRB.TIME_LIMIT:
print('total cost = {}'.format(mdl.objval))
# convert cp model solution to standard output
@@ -160,25 +159,9 @@ def optimizer_aggregation(component_data, pcb_data):
feeder_slot_result = feeder_assignment(component_data, pcb_data, component_result, cycle_result)
# === phase 2: heuristic method ===
mount_point_pos = defaultdict(list)
for pcb_idx, data in pcb_data.iterrows():
part = data['part']
part_index = component_data[component_data['part'] == part].index.tolist()[0]
mount_point_pos[part_index].append([data['x'], data['y'], pcb_idx])
for index_ in mount_point_pos.keys():
mount_point_pos[index_].sort(key=lambda x: (x[1], x[0]))
for cycle_idx, _ in enumerate(cycle_result):
for _ in range(cycle_result[cycle_idx]):
placement_result.append([-1 for _ in range(max_head_index)])
for head in range(max_head_index):
if component_result[cycle_idx][head] == -1:
continue
index_ = component_result[cycle_idx][head]
placement_result[-1][head] = mount_point_pos[index_][-1][2]
mount_point_pos[index_].pop()
head_sequence.append(dynamic_programming_cycle_path(pcb_data, placement_result[-1], feeder_slot_result[cycle_idx]))
placement_result, head_sequence = greedy_level_placing_route_generation(component_data, pcb_data,
component_result, cycle_result,
feeder_slot_result)
else:
warnings.warn('No solution found!', UserWarning)

View File

@@ -33,7 +33,7 @@ def convert_cell_2_result(pcb_data, component_data, component_cell, population):
wl = [0 for _ in range(max_head_index)] # workload
e1, e2, e3 = 1, 0.5, 1. / 6
e1, e2, e3 = 1, 2, 1. / 6
component_result, cycle_result, feeder_slot_result = [], [], []
for index in population:
@@ -101,12 +101,16 @@ def optimizer_celldivision(pcb_data, component_data, hinter=True):
golden_section = 0.618
# 获取元件元胞
point_num = len(pcb_data)
component_cell = pd.DataFrame({'index': np.arange(len(component_data)), 'points': np.zeros(len(component_data), dtype=int)})
for point_cnt in range(point_num):
part = pcb_data.loc[point_cnt, 'part']
index = np.where(component_data['part'].values == part)
component_cell.loc[index[0], 'points'] += 1
feeder_num = sum(component_data['fdn'])
component_cell = pd.DataFrame({'index': np.arange(feeder_num), 'points': np.zeros(feeder_num, dtype=int)})
cell_index = 0
for part_index, data in component_data.iterrows():
total_points, division_points = data.points, math.ceil(data.points / data.fdn)
for _ in range(data.fdn):
component_cell.loc[cell_index, 'index'] = part_index
component_cell.loc[cell_index, 'points'] = min(division_points, total_points)
total_points -= division_points
cell_index += 1
component_cell = component_cell[~component_cell['points'].isin([0])]
# component_cell.sort_values(by = "points" , inplace = True, ascending = False)

View File

@@ -1,5 +1,5 @@
from base_optimizer.optimizer_common import *
from base_optimizer.smtopt_route import *
def dynamic_programming_cycle_path(cycle_placement, cycle_points):
head_sequence = []
@@ -231,8 +231,8 @@ def cal_individual_val(component_nozzle, component_point_pos, designated_nozzle,
def convert_individual_2_result(component_data, component_point_pos, designated_nozzle, pickup_group,
pickup_group_cycle, pair_group, feeder_lane, individual):
component_result, cycle_result, feeder_slot_result = [], [], []
placement_result, head_sequence_result = [], []
# === 记录不同元件对应的槽位 ===
feeder_part_arrange = defaultdict(list)
@@ -273,26 +273,7 @@ def convert_individual_2_result(component_data, component_point_pos, designated_
pickup_cycle_result[idx][head] -= cycle
component_point_index = defaultdict(int)
for cycle_set in range(len(cycle_result)):
for cycle in range(cycle_result[cycle_set]):
placement_result.append([-1 for _ in range(max_head_index)])
mount_point = [[0, 0] for _ in range(max_head_index)]
for head in range(max_head_index):
part_index = component_result[cycle_set][head]
if part_index == -1:
continue
part = component_data.iloc[part_index]['part']
point_info = component_point_pos[part][component_point_index[part]]
placement_result[-1][head] = point_info[2]
mount_point[head] = point_info[0:2]
component_point_index[part] += 1
head_sequence_result.append(dynamic_programming_cycle_path(placement_result[-1], mount_point))
return component_result, cycle_result, feeder_slot_result, placement_result, head_sequence_result
return component_result, cycle_result, feeder_slot_result
@timer_wrapper
@@ -510,11 +491,6 @@ def optimizer_hybrid_genetic(pcb_data, component_data, hinter=True):
pop_val.append(val) # val is related to assembly time
for _ in range(n_generations):
# idx = np.argmin(pop_val)
# if len(best_pop_val) == 0 or pop_val[idx] < best_pop_val[-1]:
# best_individual = copy.deepcopy(population[idx])
# best_pop_val.append(pop_val[idx])
# min-max convert
max_val = 1.5 * max(pop_val)
convert_pop_val = list(map(lambda v: max_val - v, pop_val))
@@ -565,6 +541,15 @@ def optimizer_hybrid_genetic(pcb_data, component_data, hinter=True):
pbar.update(1)
best_individual = population[np.argmin(pop_val)]
component_result, cycle_result, feeder_slot_result = convert_individual_2_result(component_data,
component_point_pos,
designated_nozzle, pickup_group,
pickup_group_cycle, pair_group,
feeder_lane, best_individual)
placement_result, head_sequence_result = place_cluster_greedy_route_generation(component_data, pcb_data,
component_result, cycle_result,
feeder_slot_result)
return component_result, cycle_result, feeder_slot_result, placement_result, head_sequence_result
return convert_individual_2_result(component_data, component_point_pos, designated_nozzle, pickup_group,
pickup_group_cycle, pair_group, feeder_lane, best_individual)

View File

@@ -1,4 +1,5 @@
from base_optimizer.optimizer_common import *
from base_optimizer.smtopt_route import *
def head_task_model(component_data, pcb_data, hinter=True):
@@ -6,11 +7,10 @@ def head_task_model(component_data, pcb_data, hinter=True):
mdl = Model('pick_route')
mdl.setParam('Seed', 0)
mdl.setParam('OutputFlag', hinter) # set whether output the debug information
mdl.setParam('TimeLimit', 600)
mdl.setParam('TimeLimit', 1000)
H = max_head_index
I = len(component_data)
S = len(component_data)
K = len(pcb_data)
nozzle_type, component_type = [], []
@@ -29,12 +29,16 @@ def head_task_model(component_data, pcb_data, hinter=True):
M = 10000
CompOfNozzle = [[0 for _ in range(J)] for _ in range(I)] # Compatibility
component_point = [0 for _ in range(I)]
component_point, component_fdn = [0 for _ in range(I)], [0 for _ in range(I)]
for _, data in pcb_data.iterrows():
idx = component_data[component_data.part == data.part].index.tolist()[0]
nozzle = component_data.iloc[idx].nz
CompOfNozzle[idx][nozzle_type.index(nozzle)] = 1
component_point[idx] += 1
component_fdn[idx] = component_data.iloc[idx].fdn
S = sum(component_fdn)
# objective related
g = mdl.addVars(list_range(K), vtype=GRB.BINARY)
@@ -94,7 +98,7 @@ def head_task_model(component_data, pcb_data, hinter=True):
range(-(H - 1) * r, S) for k in range(K))
# feeder related
mdl.addConstrs(quicksum(f[s, i] for s in range(S)) <= 1 for i in range(I))
mdl.addConstrs(quicksum(f[s, i] for s in range(S)) <= component_fdn[i] for i in range(I))
mdl.addConstrs(quicksum(f[s, i] for i in range(I)) <= 1 for s in range(S))
mdl.addConstrs(
quicksum(x[i, k, h] * y[s, k, h] for h in range(H) for k in range(K)) >= f[s, i] for i in range(I) for s in range(S))
@@ -110,7 +114,7 @@ def head_task_model(component_data, pcb_data, hinter=True):
# objective
mdl.setObjective(Fit_cy * quicksum(g[k] for k in range(K)) + Fit_nz * quicksum(
d[k, h] for h in range(H) for k in range(K)) + Fit_pu * quicksum(
e[s, k] for s in range(-(H - 1) * r, S) for k in range(K)) + Fit_mv * head_interval * quicksum(u[k] for k in range(K)),
e[s, k] for s in range(-(H - 1) * r, S) for k in range(K)) + Fit_mv * quicksum(u[k] for k in range(K)),
GRB.MINIMIZE)
mdl.optimize()
@@ -119,19 +123,21 @@ def head_task_model(component_data, pcb_data, hinter=True):
for k in range(K):
if abs(g[k].x) < 1e-6:
continue
component_assign, feeder_slot_assign = [-1 for _ in range(H)], [-1 for _ in range(H)]
component_result.append([-1 for _ in range(H)])
feeder_slot_result.append([-1 for _ in range(H)])
cycle_result.append(1)
for h in range(H):
for i in range(I):
if abs(x[i, k, h].x) > 1e-6:
component_result[-1][h] = i
component_assign[h] = i
for s in range(S):
if abs(y[s, k, h].x) > 1e-6:
feeder_slot_result[-1][h] = slot_start + s * interval_ratio - 1
feeder_slot_assign[h] = slot_start + s * interval_ratio - 1
if sum(component_assign) != -H:
component_result.append(component_assign)
feeder_slot_result.append(feeder_slot_assign)
cycle_result.append(1)
if hinter:
print(component_result)
@@ -143,7 +149,7 @@ def place_route_model(component_data, pcb_data, component_result, feeder_slot_re
mdl = Model('place_route')
mdl.setParam('Seed', 0)
mdl.setParam('OutputFlag', hinter) # set whether output the debug information
mdl.setParam('TimeLimit', 10)
mdl.setParam('TimeLimit', 1000)
component_type = []
for _, data in component_data.iterrows():
@@ -227,7 +233,7 @@ def place_route_model(component_data, pcb_data, component_result, feeder_slot_re
else:
# there are components on the head
mdl.addConstrs(quicksum(w[p, q, k, a] for a in A_from(h) for q in range(P)) + quicksum(
w[q, p, k, a] for a in A_to(h) for q in range(P)) + y[p, k, h] + z[p, k, h] <= 4 *
w[q, p, k, a] for a in A_to(h) for q in range(P)) + y[p, k, h] + z[p, k, h] <= 2 *
CompOfPoint[component_result[k][h]][p] for p in range(P))
# each head corresponds to a maximum of one point in each cycle
@@ -362,7 +368,9 @@ def place_route_model(component_data, pcb_data, component_result, feeder_slot_re
def optimizer_mathmodel(component_data, pcb_data, hinter=True):
component_result, cycle_result, feeder_slot_result = head_task_model(component_data, pcb_data, hinter)
placement_result, head_sequence = place_route_model(component_data, pcb_data, component_result, feeder_slot_result)
# placement_result, head_sequence = greedy_placement_route_generation(component_data, pcb_data, component_result,
# cycle_result)
# placement_result, head_sequence = place_route_model(component_data, pcb_data, component_result, feeder_slot_result)
placement_result, head_sequence = place_allocate_sequence_route_generation(component_data, pcb_data,
component_result, cycle_result,
feeder_slot_result)
return component_result, cycle_result, feeder_slot_result, placement_result, head_sequence

View File

@@ -1,4 +1,6 @@
from base_optimizer.optimizer_common import *
from base_optimizer.smtopt_route import *
from base_optimizer.result_analysis import *
def list_range(start, end=None):
@@ -80,9 +82,10 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
objbst, objbnd = mdl.cbGet(GRB.Callback.MIP_OBJBST), mdl.cbGet(GRB.Callback.MIP_OBJBND)
changetime = mdl.cbGet(GRB.Callback.RUNTIME)
nonlocal pre_objbst, pre_changetime
# condition: value change
# condition: value change
if abs(objbst - 1e+100) > 1: # 避免未找到可行解提前退出
if pre_objbst and abs(pre_objbst - objbst) < 1e-3:
if pre_changetime and changetime - pre_changetime > 100 * (1 - objbnd / objbst):
if pre_changetime and changetime - pre_changetime > 90 * (1 - objbnd / objbst):
mdl.terminate()
else:
pre_changetime = changetime
@@ -147,7 +150,7 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
level += 1
L = len(cycle_assignment) if partition else len(pcb_data)
S = ratio * sum(component_feeder.values()) * 2 if len(feeder_data) == 0 else arg_slot_rng[-1] - arg_slot_rng[0] + 1 # the available feeder num
S = ratio * sum(component_feeder.values()) if len(feeder_data) == 0 else arg_slot_rng[-1] - arg_slot_rng[0] + 1 # the available feeder num
M = len(pcb_data) # a sufficiently large number (number of placement points)
HC = [[0 for _ in range(J)] for _ in range(I)]
for i in range(I):
@@ -157,12 +160,12 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
mdl = Model('SMT')
mdl.setParam('Seed', 0)
mdl.setParam('OutputFlag', hinter) # set whether output the debug information
mdl.setParam('TimeLimit', 3600 * 3)
mdl.setParam('TimeLimit', 3600)
mdl.setParam('PoolSearchMode', 2)
mdl.setParam('PoolSolutions', 3e2)
mdl.setParam('PoolSolutions', 100)
mdl.setParam('PoolGap', 1e-4)
# mdl.setParam('MIPFocus', 2)
# mdl.setParam("Heuristics", 0.5)
mdl.setParam("Heuristics", 0.5)
# Use only if other methods, including exploring the tree with the default settings, do not yield a viable solution
# mdl.setParam("ZeroObjNodes", 100)
@@ -178,7 +181,6 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
c[i, h, l] <= component_list[cpidx_2_part[i]] for i in range(I) for h in range(max_head_index) for l in
range(L))
# todo: the condition for upper limits of feeders exceed 1
f = {}
for i in range(I):
if i not in part_feederbase.keys():
@@ -227,7 +229,7 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
continue
part_feederbase[part_2_cpidx[part]] = slot
# f[slot, part_2_cpidx[part]].Start = 1
f[slot, part_2_cpidx[part]].Start = 1
slot += 1
for idx, cycle in enumerate(cycle_index):
@@ -334,13 +336,12 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
in range(L))
if reduction:
# mdl.addConstrs(WL[l] >= WL[l + 1] for l in range(L - 1))
mdl.addConstrs(WL[l] >= WL[l + 1] for l in range(L - 1))
mdl.addConstr(quicksum(WL[l] for l in range(L)) <= sum(cycle_assignment))
mdl.addConstr(quicksum(WL[l] for l in range(L)) >= math.ceil(len(pcb_data) / max_head_index))
mdl.addConstrs(quicksum(z[j, h, l] for j in range(J) for h in range(max_head_index)) >= quicksum(
z[j, h, l + 1] for j in range(J) for h in range(max_head_index)) for l in range(L - 1))
mdl.addConstrs(y[i, h, l] <= WL[l] for i in range(I) for h in range(max_head_index) for l in range(L))
mdl.addConstrs(v[s, h, l] <= WL[l] for s in range(S) for h in range(max_head_index) for l in range(L))
@@ -365,125 +366,126 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
# mdl.optimize()
# === result generation ===
nozzle_assign, component_assign = [], []
feeder_assign, cycle_assign = [], []
opt_res_list = defaultdict(OptResult)
if mdl.Status == GRB.OPTIMAL or mdl.Status == GRB.INTERRUPTED or mdl.Status == GRB.TIME_LIMIT:
# === selection from solution pool ===
component_pos, component_avg_pos = defaultdict(list), defaultdict(list)
component_pos = defaultdict(list[Point])
for _, data in pcb_data.iterrows():
component_index = component_data[component_data.part == data.part].index.tolist()[0]
component_pos[component_index].append([data.x, data.y])
component_pos[component_index].append(Point(data.x, data.y))
for i in component_pos.keys():
component_pos[i] = sorted(component_pos[i], key=lambda pos: (pos[0], pos[1]))
component_avg_pos[i] = [sum(map(lambda pos: pos[0], component_pos[i])) / len(component_pos[i]),
sum(map(lambda pos: pos[1], component_pos[i])) / len(component_pos[i])]
for part in component_pos.keys():
component_pos[part] = sorted(component_pos[part], key=lambda pos: (pos.x, pos.y))
min_dist, solution_number = None, -1
min_dist, solution_number = None, 0
for sol_counter in range(mdl.SolCount):
nozzle_assign, component_assign = [], []
feeder_assign, cycle_assign = [], []
mdl.Params.SolutionNumber = sol_counter
pos_counter = defaultdict(int)
dist = 0
cycle_placement, cycle_points = defaultdict(list), defaultdict(list)
opt_res = OptResult()
# == 转换标准的贴装头分配的解 ===
for l in range(L):
if abs(WL[l].Xn) <= 1e-4:
continue
cycle_placement[l], cycle_points[l] = [-1] * max_head_index, [None] * max_head_index
opt_res.cycle_assign.append(round(WL[l].Xn))
opt_res.component_assign.append([-1] * max_head_index)
opt_res.feeder_slot_assign.append([-1] * max_head_index)
for h in range(max_head_index):
for l in range(L):
if abs(WL[l].Xn) <= 1e-4:
continue
pos_list = []
for i in range(I):
if abs(y[i, h, l].Xn) <= 1e-4:
continue
opt_res.component_assign[-1][h] = i
for _ in range(round(WL[l].Xn)):
pos_list.append(component_pos[i][pos_counter[i]])
pos_counter[i] += 1
cycle_placement[l][h] = i
cycle_points[l][h] = [sum(map(lambda pos: pos[0], pos_list)) / len(pos_list),
sum(map(lambda pos: pos[1], pos_list)) / len(pos_list)]
for l in range(L):
if abs(WL[l].Xn) <= 1e-4:
continue
if min_dist is None or dist < min_dist:
min_dist = dist
solution_number = sol_counter
mdl.Params.SolutionNumber = solution_number
# === 更新吸嘴、元件、周期数优化结果 ===
for l in range(L):
nozzle_assign.append([-1 for _ in range(max_head_index)])
component_assign.append([-1 for _ in range(max_head_index)])
feeder_assign.append([-1 for _ in range(max_head_index)])
cycle_assign.append(round(WL[l].Xn))
if abs(WL[l].Xn) <= 1e-4:
continue
for h in range(max_head_index):
for i in range(I):
if abs(y[i, h, l].Xn - 1) < 1e-4:
component_assign[-1][h] = i
for j in range(J):
if HC[i][j]:
nozzle_assign[-1][h] = j
for s in range(S):
if abs(v[s, h, l].Xn - 1) < 1e-4 and component_assign[l][h] != -1:
feeder_assign[l][h] = s
if abs(v[s, h, l].Xn - 1) < 1e-4 and opt_res.component_assign[-1][h] != -1:
opt_res.feeder_slot_assign[-1][h] = s
# === 更新供料器分配结果 ==
component_head = defaultdict(int)
for i in range(I):
cycle_num = 0
for l, component_cycle in enumerate(component_assign):
for head, component in enumerate(component_cycle):
if component == i:
component_head[i] += cycle_assign[l] * head
cycle_num += cycle_assign[l]
component_head[i] /= cycle_num # 不同元件的加权拾取贴装头
average_pos = 0
for _, data in pcb_data.iterrows():
average_pos += (data.x - component_head[part_2_cpidx[data.part]] * head_interval)
average_pos /= len(pcb_data) # 实际贴装位置的加权平均
average_slot = 0
for l in range(L):
if abs(WL[l].Xn) <= 1e-4:
# 根据贴装头位置,转换供料器槽位
cp_avg_head, cp_sum_cycle = defaultdict(float), defaultdict(int)
for cycle, component_assign in enumerate(opt_res.component_assign):
for head, part in enumerate(component_assign):
if part == -1:
continue
min_slot, max_slot = None, None
cp_avg_head[part] += opt_res.cycle_assign[cycle] * head
cp_sum_cycle[part] += opt_res.cycle_assign[cycle]
for part, head in cp_avg_head.items():
cp_avg_head[part] = head / cp_sum_cycle[part]
avg_position = sum([data.x - cp_avg_head[part_2_cpidx[data.part]] * head_interval for _, data in
pcb_data.iterrows()]) / len(pcb_data)
avg_slot = 0
D_PU, D_PL, D_BW, D_FW = 0, 0, 0, 0
for cycle, slots in enumerate(opt_res.feeder_slot_assign):
min_slot, max_slot = max_slot_index, 0
for head, slot in enumerate(slots):
if slot == -1:
continue
min_slot = min(min_slot, slot - head * ratio)
max_slot = max(max_slot, slot - head * ratio)
avg_slot += (max_slot - min_slot) * opt_res.cycle_assign[cycle]
D_PU += (max_slot - min_slot) * slot_interval * opt_res.cycle_assign[cycle] # 拾取路径
avg_slot /= sum(opt_res.cycle_assign)
start_slot = round((avg_position + stopper_pos[0] - slotf1_pos[0]) / slot_interval + avg_slot / 2) + 1
for cycle in range(len(opt_res.feeder_slot_assign)):
for head in range(max_head_index):
if abs(WL[l].Xn) <= 1e-4 or feeder_assign[l][head] == -1:
if (slot := opt_res.feeder_slot_assign[cycle][head]) == -1:
continue
slot = feeder_assign[l][head] - head * ratio
if min_slot is None or slot < min_slot:
min_slot = slot
if max_slot is None or slot > max_slot:
max_slot = slot
average_slot += (max_slot - min_slot) * cycle_assign[l]
average_slot /= sum(cycle_assign)
start_slot = round((average_pos + stopper_pos[0] - slotf1_pos[0]) / slot_interval + average_slot / 2) + 1
opt_res.feeder_slot_assign[cycle][head] = start_slot + slot * (2 if ratio == 1 else 1)
for l in range(L):
if abs(WL[l].Xn) <= 1e-4:
component_pos_counter = defaultdict(int)
cycle_place_pos = defaultdict(list[Point])
for head in range(max_head_index):
for cycle in range(len(opt_res.cycle_assign)):
if (part := opt_res.component_assign[cycle][head]) == -1:
continue
for h in range(max_head_index):
for s in range(S):
if abs(v[s, h, l].Xn - 1) < 1e-4 and component_assign[l][h] != -1:
feeder_assign[l][h] = start_slot + s * (2 if ratio == 1 else 1)
avg_place_pos = Point(0, 0, _h=head)
for counter in range(round(opt_res.cycle_assign[cycle])):
avg_place_pos.x = (1 - 1.0 / (counter + 1)) * avg_place_pos.x + (
component_pos[part][component_pos_counter[part]].x - head * head_interval) / (
counter + 1)
avg_place_pos.y = (1 - 1.0 / (counter + 1)) * avg_place_pos.y + component_pos[part][
component_pos_counter[part]].y / (counter + 1)
component_pos_counter[part] += 1
avg_place_pos.x += stopper_pos[0]
avg_place_pos.y += stopper_pos[1]
cycle_place_pos[cycle].append(avg_place_pos)
for cycle in range(len(opt_res.cycle_assign)):
min_slot, max_slot = max_slot_index, 0
for head in range(max_head_index):
if (slot := opt_res.feeder_slot_assign[cycle][head]) == -1:
continue
min_slot = min(min_slot, slot - head * interval_ratio)
max_slot = max(max_slot, slot - head * interval_ratio)
# cycle_place_pos[cycle] = sorted(cycle_place_pos[cycle], key=lambda pt: pt.x)
pick_pos = Point(slotf1_pos[0] + (min_slot + max_slot) / 2 * slot_interval, slotf1_pos[1])
_, seq = dynamic_programming_cycle_route(cycle_place_pos[cycle], pick_pos)
head_position = [Point(0, 0) for _ in range(max_head_index)]
for point in cycle_place_pos[cycle]:
head_position[point.h] = point
for idx in range(len(seq) - 1):
h1, h2 = seq[idx], seq[idx + 1]
D_PL += max(abs(head_position[h1].x - head_position[h2].x),
abs(head_position[h1].y - head_position[h2].y)) * opt_res.cycle_assign[cycle]
# opt_res.placement_assign, opt_res.head_sequence = scan_based_placement_route_generation(component_data,
# pcb_data,
# opt_res.component_assign,
# opt_res.cycle_assign,
# opt_res.feeder_slot_assign,
# hinter=False)
# info = placement_info_evaluation(component_data, pcb_data, opt_res)
# print(f'{info.place_distance + info.pickup_distance: .3f}\t{D_PL + D_PU: .3f}')
opt_res_list[sol_counter] = opt_res
solution_number = 0
mdl.Params.SolutionNumber = 0
if hinter:
print('total cost = {}'.format(mdl.objval))
print('cycle = {}, nozzle change = {}, pick up = {}'.format(quicksum(WL[l].Xn for l in range(L)), quicksum(
@@ -496,9 +498,10 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
print('')
print('result')
print('nozzle assignment: ', nozzle_assign)
print('component assignment: ', component_assign)
print('feeder assignment: ', feeder_assign)
print('cycle assignment: ', cycle_assign)
return component_assign, feeder_assign, cycle_assign
print('component assignment: ', opt_res_list[solution_number].component_assign)
print('feeder assignment: ', opt_res_list[solution_number].feeder_slot_assign)
print('cycle assignment: ', opt_res_list[solution_number].cycle_assign)
return opt_res_list[solution_number].component_assign, opt_res_list[solution_number].feeder_slot_assign, \
opt_res_list[solution_number].cycle_assign

File diff suppressed because it is too large Load Diff

View File

@@ -161,8 +161,27 @@ def load_data(filename: str, load_feeder=False, auto_register=True):
feeder_data = pd.DataFrame(columns=feeder_columns)
else:
feeder_data = pd.DataFrame(columns=feeder_columns)
for idx, data in feeder_data.iterrows():
feeder_data.at[idx, 'slot'] = int(data['slot'][1:])
feeder_data.sort_values(by='slot', ascending=True, inplace=True, ignore_index=True)
feeder_data_check = defaultdict(str)
for _, data in feeder_data.iterrows():
feeder_data_check[data.slot] = data.part
for machine_index in range(machine_num):
for idx, data in pcb_data[machine_index].iterrows():
slot, part = data.fdr.split(' ')
slot = int(slot[1:])
if feeder_data_check[slot] == '':
feeder_data_check[slot] = part
if feeder_data_check[slot] != part:
warning_info = f'conflict feeder registration PCB: {data.fdr}, BASE: F{slot} {feeder_data_check[slot]}'
# warnings.warn(warning_info, UserWarning)
return pcb_data, component_data, feeder_data

View File

@@ -6,7 +6,7 @@ def exact_assembly_time(pcb_data, component_data):
feeder_data = pd.DataFrame(columns=['slot', 'part'])
component_result, cycle_result, feeder_slot_result = feeder_priority_assignment(component_data, pcb_data,
feeder_data, hinter=False)
placement_result, head_sequence_result = greedy_placement_route_generation(component_data, pcb_data,
placement_result, head_sequence_result = place_allocate_sequence_route_generation(component_data, pcb_data,
component_result, cycle_result,
feeder_slot_result, hinter=False)
opt_res = OptResult(component_result, cycle_result, feeder_slot_result, placement_result, head_sequence_result)
@@ -19,7 +19,7 @@ def error_info(pred_val, real_val, type='train'):
absolute_error = np.array([])
for idx, (t1, t2) in enumerate(np.nditer([pred_val, real_val])):
absolute_error = np.append(absolute_error, abs(t1 - t2) / (t2 + 1e-10) * 100)
if absolute_error[-1] > 15:
if absolute_error[-1] > 10:
print(f'\033[0;31;31midx: {idx + 1: d}, net: {t1: .3f}, real: {t2: .3f}, '
f'gap: {absolute_error[-1]: .3f}\033[0m')
@@ -561,6 +561,8 @@ class MetricEstimator(Estimator):
def training(self, params):
x_fit, y_fit = data_mgr.metric('opt/' + params.train_file)
self.lr.fit(x_fit, y_fit)
y_predict = self.lr.predict(x_fit)
error_info(y_fit, y_predict, 'train')
print(self.lr.coef_)
def testing(self, params):
@@ -578,7 +580,7 @@ if __name__ == '__main__':
parser = argparse.ArgumentParser(description='network training implementation')
# parser.add_argument('--train', default=True, type=bool, help='determine whether training the network')
parser.add_argument('--save', default=True, type=bool,
parser.add_argument('--save', default=False, type=bool,
help='determine whether saving the parameters of network, linear regression model, etc.')
parser.add_argument('--overwrite', default=False, type=bool,
help='determine whether overwriting the training and testing data')
@@ -588,7 +590,7 @@ if __name__ == '__main__':
parser.add_argument('--batch_size', default=2000, type=int, help='size of training batch')
parser.add_argument('--lr', default=1e-5, type=float, help='learning rate for the network')
parser.add_argument('--model', default='neural-network', help='method for assembly time estimation')
parser.add_argument('--machine_optimizer', default='feeder-scan', type=str, help='optimizer for single machine')
parser.add_argument('--machine_optimizer', default='feeder-priority', type=str, help='optimizer for single machine')
params = parser.parse_args()
data_mgr = DataMgr()
@@ -606,13 +608,13 @@ if __name__ == '__main__':
# data_mgr.remover() # remove the last saved data
# data_mgr.saver('data/' + file_name, pcb_data) # save new data
info = base_optimizer(1, pcb_data, component_data, pd.DataFrame(columns=['slot', 'part', 'arg']),
params, hinter=True)
info = base_optimizer(1, pcb_data, component_data, pd.DataFrame(columns=['slot', 'part']), params,
hinter=True)
data_mgr.recorder(f, info, pcb_data, component_data)
f.close()
estimator = MetricEstimator()
estimator = NeuralEstimator()
estimator.training(params)
estimator.testing(params)

View File

@@ -73,9 +73,9 @@ class DataMgr:
lineinfo += '\t' + '{:.3f}'.format(pcb_data['x'].max() - pcb_data['x'].min()) + '\t' + '{:.3f}'.format(
pcb_data['y'].max() - pcb_data['y'].min())
# part_position = defaultdict(list)
# for _, data in pcb_data.iterrows():
# part_position[data['part']].append([data['x'], data['y']])
part_position = defaultdict(list)
for _, data in pcb_data.iterrows():
part_position[data['part']].append([data['x'], data['y']])
point_counter, component_counter = 0, 0
nozzle_type = set()
@@ -94,8 +94,8 @@ class DataMgr:
if data.points == 0:
continue
lineinfo += '\t' + data.part + '\t' + data.nz + '\t' + str(data.points)
# lineinfo += '\t' + '{:.3f}'.format(np.ptp([pos[0] for pos in part_position[data.part]]))
# lineinfo += '\t' + '{:.3f}'.format(np.ptp([pos[1] for pos in part_position[data.part]]))
lineinfo += '\t' + '{:.3f}'.format(np.ptp([pos[0] for pos in part_position[data.part]]))
lineinfo += '\t' + '{:.3f}'.format(np.ptp([pos[1] for pos in part_position[data.part]]))
lineinfo += '\n'
file_handle.write(lineinfo)
@@ -173,7 +173,7 @@ class DataMgr:
return data
def heuristic_objective(self, cp_points, cp_nozzle):
if len(cp_points.keys()) == 0:
if len(cp_points.keys()) or sum(cp_points.values()) == 0:
return 0, 0, 0, 0
nozzle_heads, nozzle_points = defaultdict(int), defaultdict(int)
for idx, points in cp_points.items():
@@ -417,18 +417,18 @@ class DataMgr:
# assembly time data
time_data.append(float(items[0]))
cp_data.append([cp_points, cp_nozzle, board_width, board_height])
cp_data.append([cp_points, cp_nozzle, board_width, board_height, cycle, nozzle_change_data, pickup_data])
line = file.readline()
if data_filter:
cph_data = [point_data[idx] / time_data[idx] * 3600 for idx in range(len(time_data))]
# cph_data = [point_data[idx] / time_data[idx] * 3600 for idx in range(len(time_data))]
w_quart = 0.6
Q1, Q3 = np.percentile(np.array(cph_data), 25), np.percentile(np.array(cph_data), 75)
w_quart = 0.3
Q1, Q3 = np.percentile(np.array(time_data), 25), np.percentile(np.array(time_data), 75)
indices = [i for i in range(len(cph_data)) if
Q1 - w_quart * (Q3 - Q1) <= cph_data[i] <= Q3 + w_quart * (Q3 - Q1)]
indices = [i for i in range(len(time_data)) if
Q1 - w_quart * (Q3 - Q1) <= time_data[i] <= Q3 + w_quart * (Q3 - Q1)]
filter_cp_data, filter_time_data = [], []
for idx in indices:
@@ -465,8 +465,8 @@ class DataMgr:
def neural_encode(self, input_data):
train_data = []
for cp_points, cp_nozzle, board_width, board_height in input_data:
train_data.append(self.encode(cp_points, cp_nozzle, board_width, board_height))
for cp_points, cp_nozzle, board_width, board_height, cy, nz, pu in input_data:
train_data.append(self.encode(cp_points, cp_nozzle, board_width, board_height, cy, nz, pu))
return train_data
def get_feature(self):

View File

@@ -278,7 +278,7 @@ def cal_individual_val(heuristic_map, cp_index, cp_points, cp_nozzle, cp_feeders
def line_optimizer_hyperheuristic(component_data, pcb_data, machine_number):
heuristic_map = {
'p': LeastPoints,
'n': LeastNzChange,
'n': LeastNzTypes,
'c': LeastCpTypes,
'r': LeastCpNzRatio,
'k': LeastCycle,
@@ -411,12 +411,13 @@ def line_optimizer_hyperheuristic(component_data, pcb_data, machine_number):
best_component_list = component_list.copy()
machine_cp_points = convert_assignment_result(heuristic_map, cp_index, cp_points, cp_nozzle, cp_feeders,
best_component_list, best_heuristic_list, machine_number)
best_component_list, best_heuristic_list, machine_number, is_opt=True)
assignment_result = [[0 for _ in range(len(component_data))] for _ in range(machine_number)]
for machine_idx in range(machine_number):
for idx in machine_cp_points[machine_idx]:
assignment_result[machine_idx][cp_index[idx]] += cp_points[idx]
return assignment_result

View File

@@ -7,7 +7,7 @@ def line_optimizer_model(component_data, pcb_data, machine_num, hinter=True):
mdl = Model('pcb assembly line optimizer')
mdl.setParam('Seed', 0)
mdl.setParam('OutputFlag', hinter) # set whether output the debug information
mdl.setParam('TimeLimit', 600 * 3)
mdl.setParam('TimeLimit', 1000)
nozzle_type, component_type = [], []
for _, data in component_data.iterrows():
@@ -22,9 +22,10 @@ def line_optimizer_model(component_data, pcb_data, machine_num, hinter=True):
H = max_head_index
I = len(component_data)
S = min(len(component_data) * ratio, 60)
K = math.ceil(len(pcb_data) * 1.0 / H / M) + 1
# K = 3
S = sum([data.fdn * ratio for _, data in component_data.iterrows()])
K = math.ceil(len(pcb_data) * 1.0 / M) + 1
# K = len(pcb_data)
CompOfNozzle = [[0 for _ in range(J)] for _ in range(I)] # Compatibility
component_point = [0 for _ in range(I)]
@@ -35,15 +36,17 @@ def line_optimizer_model(component_data, pcb_data, machine_num, hinter=True):
# objective related
g = mdl.addVars(list_range(K), list_range(M), vtype=GRB.BINARY)
d = mdl.addVars(list_range(K), list_range(H), list_range(M), lb=0, vtype=GRB.CONTINUOUS)
d = mdl.addVars(list_range(K), list_range(H), list_range(M), vtype=GRB.INTEGER)
u = mdl.addVars(list_range(I), list_range(K), list_range(H), list_range(M), vtype=GRB.BINARY)
v = mdl.addVars(list_range(S), list_range(K), list_range(H), list_range(M), vtype=GRB.BINARY)
d_plus = mdl.addVars(list_range(J), list_range(K), list_range(H), list_range(M), lb=0, vtype=GRB.CONTINUOUS)
d_minus = mdl.addVars(list_range(J), list_range(K), list_range(H), list_range(M), lb=0, vtype=GRB.CONTINUOUS)
w = mdl.addVars(list_range(K), list_range(M), vtype=GRB.CONTINUOUS)
w = mdl.addVars(list_range(J), list_range(K), list_range(H), list_range(M), vtype=GRB.BINARY)
d_plus = mdl.addVars(list_range(J), list_range(K), list_range(H), list_range(M), vtype=GRB.CONTINUOUS)
d_minus = mdl.addVars(list_range(J), list_range(K), list_range(H), list_range(M), vtype=GRB.CONTINUOUS)
z = mdl.addVars(list_range(K), list_range(M), vtype=GRB.INTEGER)
e = mdl.addVars(list_range(-(H - 1) * ratio, S), list_range(K), list_range(M), vtype=GRB.BINARY)
f = mdl.addVars(list_range(S), list_range(I), list_range(M), vtype=GRB.BINARY, name='')
t = mdl.addVars(list_range(M), lb=0, ub=N, vtype=GRB.CONTINUOUS)
obj = mdl.addVar(lb=0, ub=N, vtype=GRB.CONTINUOUS)
mdl.addConstrs(g[k, m] >= g[k + 1, m] for k in range(K - 1) for m in range(M))
@@ -52,24 +55,26 @@ def line_optimizer_model(component_data, pcb_data, machine_num, hinter=True):
quicksum(u[i, k, h, m] for i in range(I)) <= g[k, m] for k in range(K) for h in range(H) for m in range(M))
# nozzle no more than 1 for head h and cycle k
mdl.addConstrs(quicksum(CompOfNozzle[i][j] * u[i, k, h, m] for i in range(I) for j in range(J)) <= 1 for k
in range(K) for h in range(H) for m in range(M))
mdl.addConstrs(quicksum(w[j, k, h, m] for j in range(J)) <= 1 for k in range(K) for h in range(H) for m in range(M))
# work completion
mdl.addConstrs(
quicksum(u[i, k, h, m] for k in range(K) for h in range(H) for m in range(M)) == component_point[i] for i in
range(I))
# nozzle change
mdl.addConstrs(quicksum(CompOfNozzle[i][j] * u[i, k, h, m] for i in range(I)) - quicksum(
CompOfNozzle[i][j] * u[i, k + 1, h, m] for i in range(I)) == d_plus[j, k, h, m] - d_minus[j, k, h, m] for k in
mdl.addConstrs(
u[i, k, h, m] <= quicksum(CompOfNozzle[i][j] * w[j, k, h, m] for j in range(J)) for i in range(I) for k in
range(K) for h in range(H) for m in range(M))
mdl.addConstrs(w[j, k, h, m] - w[j, k + 1, h, m] == d_plus[j, k, h, m] - d_minus[j, k, h, m] for k in
range(K - 1) for j in range(J) for h in range(H) for m in range(M))
mdl.addConstrs(quicksum(CompOfNozzle[i][j] * u[i, K - 1, h, m] for i in range(I)) - quicksum(
CompOfNozzle[i][j] * u[i, 0, h, m] for i in range(I)) == d_plus[j, K - 1, h, m] - d_minus[j, K - 1, h, m] for j
in range(J) for h in range(H) for m in range(M))
mdl.addConstrs(w[j, 0, h, m] - w[j, K - 1, h, m] == d_plus[j, K - 1, h, m] - d_minus[j, K - 1, h, m]
for j in range(J) for h in range(H) for m in range(M))
mdl.addConstrs(2 * d[k, h, m] == quicksum(d_plus[j, k, h, m] for j in range(J)) + quicksum(
d_minus[j, k, h, m] for j in range(J)) - 1 for k in range(K) for h in range(H) for m in range(M))
mdl.addConstrs(d[k, h, m] == quicksum(d_plus[j, k, h, m] for j in range(J)) + quicksum(
d_minus[j, k, h, m] for j in range(J)) for k in range(K) for h in range(H) for m in range(M))
# simultaneous pick
for s in range(-(H - 1) * ratio, S):
@@ -99,25 +104,29 @@ def line_optimizer_model(component_data, pcb_data, machine_num, hinter=True):
for s in range(S) for m in range(M))
# pickup movement
mdl.addConstrs(w[k, m] >= s1 * e[s1, k, m] - s2 * e[s2, k, m] + N * (e[s1, k, m] + e[s2, k, m] - 2) for s1 in
mdl.addConstrs(z[k, m] >= s1 * e[s1, k, m] - s2 * e[s2, k, m] + N * (e[s1, k, m] + e[s2, k, m] - 2) for s1 in
range(-(H - 1) * ratio, S) for s2 in range(-(H - 1) * ratio, S) for k in range(K) for m in range(M))
# objective
mdl.addConstrs(obj >= Fit_cy * quicksum(g[k, m] for k in range(K)) + Fit_nz * 2 * quicksum(
mdl.addConstrs(t[m] == Fit_cy * quicksum(g[k, m] for k in range(K)) + Fit_nz * quicksum(
d[k, h, m] for h in range(H) for k in range(K)) + Fit_pu * quicksum(
e[s, k, m] for s in range(-(H - 1) * ratio, S) for k in range(K)) + Fit_pl * quicksum(
u[i, k, h, m] for i in range(I) for k in range(K) for h in range(H)) + Fit_mv * head_interval * quicksum(
w[k, m] for k in range(K)) for m in range(M))
u[i, k, h, m] for i in range(I) for k in range(K) for h in range(H)) + Fit_mv * quicksum(
z[k, m] for k in range(K)) for m in range(M))
for m in range(M - 1):
mdl.addConstr(t[m] >= t[m + 1])
mdl.addConstrs(obj >= t[m] for m in range(M))
mdl.setObjective(obj, GRB.MINIMIZE)
mdl.optimize()
for m in range(M):
print(f'machine {m} : cycle : {sum(g[k, m].x for k in range(K))}, '
f'nozzle change : {sum(d[k, h, m].x for h in range(H) for k in range(K))}, '
f'pick up : {sum(e[s, k, m].x for s in range(-(H - 1) * ratio, S) for k in range(K))}, '
f'placement : {sum(u[i, k, h, m].x for i in range(I) for k in range(K) for h in range(H))}, '
f'pick movement : {sum(w[k, m].x for k in range(K))}')
f'pick movement : {sum(z[k, m].x for k in range(K))}')
pcb_part_indices = defaultdict(list)
for idx, data in pcb_data.iterrows():
@@ -162,19 +171,22 @@ def line_optimizer_model(component_data, pcb_data, machine_num, hinter=True):
average_pos = round(
(sum(head_place_pos) / len(head_place_pos) + stopper_pos[0] - slotf1_pos[0] + 1) / slot_interval)
print(f'average_pos: {average_pos}')
for k in range(len(feeder_slot_result)):
for h in range(H):
if feeder_slot_result[k][h] == -1:
continue
feeder_slot_result[k][h] = feeder_slot_result[k][h] * 2 + average_pos
placement_result, head_sequence = greedy_placement_route_generation(partial_component_data, partial_pcb_data,
placement_result, head_sequence = place_allocate_sequence_route_generation(partial_component_data,
partial_pcb_data,
component_result, cycle_result,
feeder_slot_result, hinter=False)
print('----- Placement machine ' + str(m + 1) + ' ----- ')
opt_res = OptResult(component_result, cycle_result, feeder_slot_result, placement_result, head_sequence)
info = placement_info_evaluation(partial_component_data, partial_pcb_data, opt_res, hinter=False)
info = placement_info_evaluation(partial_component_data, partial_pcb_data, opt_res, hinter=hinter)
if hinter:
print('----- Placement machine ' + str(m + 1) + ' ----- ')
optimization_assign_result(partial_component_data, partial_pcb_data, opt_res, nozzle_hinter=True,
component_hinter=True, feeder_hinter=True)
info.print()

View File

@@ -291,6 +291,328 @@ def evolutionary_component_assignment(pcb_data, component_data, machine_number,
return min(pop_val), population[np.argmin(pop_val)]
class SpiderMonkeyOpt:
def __init__(self, pop_size, pcb_data, component_data, machine_number, estimator):
self.PcbData = pcb_data
self.ComponentData = component_data
self.Estimator = estimator
self.PopSize = pop_size
self.LocalLimit = pop_size
self.GlobalLimit = pop_size
self.MachineNum = machine_number
self.GroupSize = 0
# self.Dim = sum(data.fdn for _, data in component_data.iterrows()) + machine_number
self.CpPoints = defaultdict(int)
self.CpIndex = defaultdict(int)
self.CpNozzle = defaultdict(str)
self.Dim = 0
for cp_idx, data in component_data.iterrows():
# cp_feeders[cp_idx] = data.fdn
division_data = copy.deepcopy(data)
feeder_limit, total_points = division_data.fdn, division_data.points
surplus_points = total_points % feeder_limit
for _ in range(feeder_limit):
division_data.fdn, division_data.points = 1, math.floor(total_points / feeder_limit)
if surplus_points:
division_data.points += 1
surplus_points -= 1
self.CpPoints[self.Dim], self.CpNozzle[self.Dim] = division_data.points, division_data.nz
self.CpIndex[self.Dim] = cp_idx
self.Dim += 1
self.Dim += machine_number
component_list = list(range(len(self.CpPoints)))
self.GenPosition = []
self.GenPopVal = []
for _ in range(self.PopSize):
random.shuffle(component_list)
self.GenPosition.append(component_list.copy())
idx, prev = 0, 0
div = random.sample(range(len(component_list)), self.MachineNum - 1)
div.append(len(component_list))
div.sort(reverse=False)
for _ in range(1, self.MachineNum + 1):
self.GenPosition[-1].append(div[idx] - prev)
prev = div[idx]
idx += 1
self.GenPopVal.append(self.CalIndividualVal(self.GenPosition[-1]))
self.GroupPoint = [[0 for _ in range(2)] for _ in range(self.PopSize)]
self.GroupPart = 1
self.GenProb = None
self.GlobalMin = self.GenPopVal[0]
self.GlobalLeaderPosition = self.GenPosition[0].copy()
self.GlobalLimitCount = 0
self.LocalMin = np.ones(self.PopSize) * 1e10
self.LocalLeaderPosition = [[0 for _ in range(self.Dim)] for _ in range(self.PopSize)]
self.LocalLimitCount = [0 for _ in range(self.PopSize)]
for k in range(self.GroupSize):
self.LocalMin[k] = self.GenPopVal[int(self.GroupPoint[k, 0])]
self.LocalLeaderPosition[k,:] = self.GenPosition[int(self.GroupPoint[k, 0]),:]
self.CrossoverRatio = 0.1
def GlobalLearning(self):
GlobalTrial = self.GlobalMin
for i in range(self.PopSize):
if self.GenPopVal[i] < self.GlobalMin:
self.GlobalMin = self.GenPopVal[i]
self.GlobalLeaderPosition = self.GenPosition[i].copy()
if math.fabs(GlobalTrial - self.GlobalMin) < 1e-5:
self.GlobalLimitCount = self.GlobalLimitCount + 1
else:
self.GlobalLimitCount = 0
def LocalLearning(self):
OldMin = np.zeros(self.PopSize)
for k in range(self.GroupSize):
OldMin[k] = self.LocalMin[k]
for k in range(self.GroupSize):
i = int(self.GroupPoint[k][0])
while i <= int(self.GroupPoint[k][1]):
if self.GenPopVal[i] < self.LocalMin[k]:
self.LocalMin[k] = self.GenPopVal[i]
self.LocalLeaderPosition[k] = self.GenPosition[i].copy()
i = i + 1
for k in range(self.GroupSize):
if math.fabs(OldMin[k] - self.LocalMin[k]) < 1e-5:
self.LocalLimitCount[k] = self.LocalLimitCount[k] + 1
else:
self.LocalLimitCount[k] = 0
def CalculateProbabilities(self):
self.GenProb = [0 for _ in range(self.PopSize)]
MaxVal = self.GenPopVal[0]
i = 1
while i < self.PopSize:
if self.GenPopVal[i] > MaxVal:
MaxVal = self.GenPopVal[i]
i += 1
for i in range(self.PopSize):
self.GenProb[i] = (0.9 * (self.GenPopVal[i] / MaxVal)) + 0.1
def LocalLeaderPhase(self, k):
lo = int(self.GroupPoint[k][0])
hi = int(self.GroupPoint[k][1])
i = lo
while i <= hi:
NewGene1, NewGene2 = self.CrossoverOperation(self.GenPosition[i], self.LocalLeaderPosition[k])
NewGeneVal1, NewGeneVal2 = self.CalIndividualVal(NewGene1), self.CalIndividualVal(NewGene2)
if NewGeneVal1 < self.GenPopVal[i]:
self.GenPosition[i] = NewGene1
self.GenPopVal[i] = NewGeneVal1
if NewGeneVal2 < self.GenPopVal[i]:
self.GenPosition[i] = NewGene2
self.GenPopVal[i] = NewGeneVal2
i += 1
def GlobalLeaderPhase(self, k):
lo = int(self.GroupPoint[k][0])
hi = int(self.GroupPoint[k][1])
i = lo
l = lo
while l < hi:
if random.random() < self.GenProb[i]:
l += 1
NewGene1, NewGene2 = self.CrossoverOperation(self.GenPosition[i], self.GlobalLeaderPosition)
NewGeneVal1, NewGeneVal2 = self.CalIndividualVal(NewGene1), self.CalIndividualVal(NewGene2)
if NewGeneVal1 < self.GenPopVal[i]:
self.GenPosition[i] = NewGene1
self.GenPopVal[i] = NewGeneVal1
if NewGeneVal2 < self.GenPopVal[i]:
self.GenPosition[i] = NewGene2
self.GenPopVal[i] = NewGeneVal2
i += 1
if i == hi:
i = lo
def LocalLeaderDecision(self):
for k in range(self.GroupSize):
if self.LocalLimitCount[k] > self.LocalLimit:
i = self.GroupPoint[k][0]
while i <= int(self.GroupPoint[k][1]):
if random.random() >= self.CrossoverRatio:
NewGenPosition = list(range(self.Dim - self.MachineNum))
random.shuffle(NewGenPosition)
idx, prev = 0, 0
div = random.sample(range(len(NewGenPosition)), self.MachineNum - 1)
div.append(len(NewGenPosition))
div.sort(reverse=False)
for _ in range(1, self.MachineNum + 1):
NewGenPosition.append(div[idx] - prev)
prev = div[idx]
idx += 1
NewGenVal = self.CalIndividualVal(NewGenPosition)
if NewGenVal < self.GenPopVal[i]:
self.GenPosition[i] = NewGenPosition.copy()
self.GenPopVal[i] = NewGenVal
else:
NewGene1, NewGene2 = self.CrossoverOperation(self.GenPosition[i], self.GlobalLeaderPosition)
NewGeneVal1, NewGeneVal2 = self.CalIndividualVal(NewGene1), self.CalIndividualVal(NewGene2)
if NewGeneVal1 < self.GenPopVal[i]:
self.GenPosition[i] = NewGene1.copy()
self.GenPopVal[i] = NewGeneVal1
if NewGeneVal2 < self.GenPopVal[i]:
self.GenPosition[i] = NewGene2.copy()
self.GenPopVal[i] = NewGeneVal2
i += 1
self.LocalLimitCount[k] = 0
def GlobalLeaderDecision(self):
if self.GlobalLimitCount> self.GlobalLimit:
self.GroupPart += 1
self.GlobalLimitCount = 0
self.CreateGroup()
self.LocalLearning()
def CreateGroup(self):
g = 0
lo = 0
while lo < self.PopSize:
hi = lo + int(self.PopSize / self.GroupPart)
self.GroupPoint[g][0] = lo
self.GroupPoint[g][1] = hi
if self.PopSize - hi < int(self.PopSize / self.GroupPart):
self.GroupPoint[g][1] = (self.PopSize - 1)
g = g + 1
lo = hi + 1
self.GroupSize = g
def CrossoverOperation(self, gene1, gene2):
len_ = len(gene1)
sub1, sub2 = partially_mapped_crossover(gene1[0: len_ - self.MachineNum], gene2[0: len_ - self.MachineNum])
pos1, pos2 = random.randint(0, self.MachineNum - 1), random.randint(0, self.MachineNum - 1)
machine_assign1, machine_assign2 = gene1[len_ - self.MachineNum:], gene2[len_ - self.MachineNum:]
machine_assign1[pos1], machine_assign2[pos2] = machine_assign2[pos2], machine_assign1[pos1]
while sum(machine_assign1) != len_ - self.MachineNum:
machine_idx = random.randint(0, self.MachineNum - 1)
if machine_assign1[machine_idx] == 0:
continue
if sum(machine_assign1) > len_ - self.MachineNum:
machine_assign1[machine_idx] -= 1
else:
machine_assign1[machine_idx] += 1
while sum(machine_assign2) != len_ - self.MachineNum:
machine_idx = random.randint(0, self.MachineNum - 1)
if machine_assign2[machine_idx] == 0:
continue
if sum(machine_assign2) > len_ - self.MachineNum:
machine_assign2[machine_idx] -= 1
else:
machine_assign2[machine_idx] += 1
sub1.extend(machine_assign1)
sub2.extend(machine_assign2)
return sub1, sub2
def CalIndividualVal(self, gene):
ComponentNum = len(self.ComponentData)
assignment_result = [[0 for _ in range(ComponentNum)] for _ in range(self.MachineNum)]
idx, machine_index = 0, 0
for num in range(self.Dim - self.MachineNum, self.Dim):
for _ in range(gene[num]):
assignment_result[machine_index][self.CpIndex[gene[idx]]] += self.CpPoints[gene[idx]]
idx += 1
machine_index += 1
val = 0
cp_items = converter(self.PcbData, self.ComponentData, assignment_result)
for machine_index in range(self.MachineNum):
cp_points, cp_nozzle, board_width, board_height = cp_items[machine_index]
val = max(val, self.Estimator.predict(cp_points, cp_nozzle, board_width, board_height))
return val
def spider_monkey_component_assignment(pcb_data, component_data, machine_number, estimator):
population_size, iteration_number = 20, 50
smo = SpiderMonkeyOpt(population_size, pcb_data, component_data, machine_number, estimator)
# ========================== Calling: GlobalLearning() ======================== #
smo.GlobalLearning()
# ========================== Calling: create_group() ========================== #
smo.CreateGroup()
# ========================= Calling: LocalLearning() ========================== #
smo.LocalLearning()
# ================================= Looping ================================== #
with tqdm(total=iteration_number) as pbar:
pbar.set_description('spider monkey algorithm process for PCB assembly line balance')
for _ in range(iteration_number):
for k in range(smo.GroupSize):
# ==================== Calling: LocalLeaderPhase() =================== #
smo.LocalLeaderPhase(k)
# =================== Calling: CalculateProbabilities() ================== #
smo.CalculateProbabilities()
for k in range(smo.GroupSize):
# ==================== Calling: GlobalLeaderPhase() ================== #
smo.GlobalLeaderPhase(k)
# ======================= Calling: GlobalLearning() ====================== #
smo.GlobalLearning()
# ======================= Calling: LocalLearning() ======================= #
smo.LocalLearning()
# ================== Calling: LocalLeaderDecision() ====================== #
smo.LocalLeaderDecision()
# ===================== Calling: GlobalLeaderDecision() ================== #
smo.GlobalLeaderDecision()
pbar.update(1)
assignment_result = [[0 for _ in range(len(component_data))] for _ in range(machine_number)]
idx, machine_index = 0, 0
for num in range(smo.Dim - machine_number, smo.Dim):
for _ in range(smo.GlobalLeaderPosition[num]):
assignment_result[machine_index][smo.CpIndex[smo.GlobalLeaderPosition[idx]]] += \
smo.CpPoints[smo.GlobalLeaderPosition[idx]]
idx += 1
machine_index += 1
return smo.GlobalMin, assignment_result
@timer_wrapper
def line_optimizer_reconfiguration(component_data, pcb_data, machine_number):
# === assignment of heads to modules is omitted ===
@@ -303,9 +625,8 @@ def line_optimizer_reconfiguration(component_data, pcb_data, machine_number):
print('random component allocation algorithm process for PCB assembly line balance')
val, assignment = random_component_assignment(pcb_data, component_data, machine_number, estimator)
elif i == 1:
# brute force
# which is proved to be useless, since it only ran in reasonable time for the smaller test instances
continue
# spider monkey
val, assignment = spider_monkey_component_assignment(pcb_data, component_data, machine_number, estimator)
elif i == 2:
# local search
print('local search component allocation algorithm process for PCB assembly line balance')
@@ -314,7 +635,8 @@ def line_optimizer_reconfiguration(component_data, pcb_data, machine_number):
# evolutionary
val, assignment = evolutionary_component_assignment(pcb_data, component_data, machine_number, estimator)
else:
# greedy: unclear description
# brute force
# which is proved to be useless, since it only ran in reasonable time for the smaller test instances
continue
if optimal_val is None or val < optimal_val:
@@ -324,3 +646,5 @@ def line_optimizer_reconfiguration(component_data, pcb_data, machine_number):
raise Exception('no feasible solution! ')
return optimal_assignment

View File

@@ -1,7 +1,3 @@
import time
import pandas as pd
from dataloader import *
from lineopt_genetic import line_optimizer_genetic
from lineopt_heuristic import line_optimizer_heuristic
@@ -12,9 +8,9 @@ from lineopt_model import line_optimizer_model
from base_optimizer.optimizer_interface import *
def optimizer(pcb_data, component_data, feeder_data, params):
def optimizer(pcb_data, component_data, feeder_data, params, hinter=True):
if params.machine_number == 1:
assembly_info = [base_optimizer(1, pcb_data, component_data, feeder_data, params, hinter=True)]
assembly_info = [base_optimizer(1, pcb_data, component_data, feeder_data, params, hinter=hinter)]
return assembly_info
if params.line_optimizer == 'hyper-heuristic' or params.line_optimizer == 'heuristic' or params.line_optimizer \
@@ -33,7 +29,7 @@ def optimizer(pcb_data, component_data, feeder_data, params):
for machine_index in range(params.machine_number):
assembly_info.append(base_optimizer(machine_index + 1, partial_pcb_data[machine_index],
partial_component_data[machine_index],
pd.DataFrame(columns=['slot', 'part']), params, hinter=True))
pd.DataFrame(columns=['slot', 'part']), params, hinter=hinter))
elif params.line_optimizer == 'mip-model':
assembly_info = line_optimizer_model(component_data, pcb_data, params.machine_number)
else:
@@ -49,16 +45,14 @@ def main():
parser.add_argument('--mode', default=1, type=int, help='mode: 0 -directly load pcb data without optimization '
'for data analysis, 1 -optimize pcb data, 2 -batch test')
parser.add_argument('--filename', default='PCB.txt', type=str, help='load pcb data')
# parser.add_argument('--filename', default='chapter3-2/PCB2-8 Arg1.txt', type=str, help='load pcb data')
parser.add_argument('--comp_register', default=1, type=int, help='register the component according the pcb data')
parser.add_argument('--machine_number', default=1, type=int, help='the number of machine in the assembly line')
# parser.add_argument('--machine_optimizer', default='mip-model', type=str, help='optimizer for single machine')
parser.add_argument('--machine_number', default=3, type=int, help='the number of machine in the assembly line')
parser.add_argument('--machine_optimizer', default='feeder-priority', type=str, help='optimizer for single machine')
parser.add_argument('--line_optimizer', default='hyper-heuristic', type=str, help='optimizer for PCB assembly line')
# parser.add_argument('--line_optimizer', default='model', type=str, help='optimizer for PCB assembly line')
parser.add_argument('--feeder_limit', default=1, type=int, help='the upper feeder limit for each type of component')
parser.add_argument('--save', default=0, type=int, help='save the optimization result')
parser.add_argument('--save_suffix', default='(10)', type=str, help='load pcb data')
parser.add_argument('--save_suffix', default='(1)', type=str, help='load pcb data')
params = parser.parse_args()
# 结果输出显示所有行和列
@@ -69,6 +63,7 @@ def main():
assembly_info = []
for machine_index in range(len(partial_pcb_data)):
opt_res = convert_pcbdata_to_result(partial_pcb_data[machine_index], partial_component_data[machine_index])
info = placement_info_evaluation(partial_component_data[machine_index], partial_pcb_data[machine_index],
opt_res)
assembly_info.append(info)
@@ -76,6 +71,7 @@ def main():
nozzle_hinter=True, component_hinter=True, feeder_hinter=True)
info.print()
if params.save:
output_optimize_result(f'result/{params.filename[:-4]}-T-Solution-M0{machine_index + 1}',
partial_component_data[machine_index], partial_pcb_data[machine_index], opt_res)
@@ -89,7 +85,6 @@ def main():
f'standard deviation: {np.std([info.total_time for info in assembly_info]): .3f}')
elif params.mode == 1:
# sys.stdout = open(f'record/{params.filename[:-4]}-{params.line_optimizer}.txt', 'w')
# 加载PCB数据
partial_pcb_data, partial_component_data, feeder_data = load_data(params.filename, load_feeder=True)
@@ -108,28 +103,33 @@ def main():
print(f'finial assembly time: {max(info.total_time for info in assembly_info): .3f} s, '
f'standard deviation: {np.std([info.total_time for info in assembly_info]): .3f}')
elif params.mode == 2:
# sys.stdout = open(f'record/dissertation-experiment.txt', 'w')
machine_optimizer = ['two-phase', 'hybrid-genetic', 'cell-division', 'feeder-priority', 'aggregation']
running_round = 10
opt_columns = ['Cycle', 'Pick', 'Nozzle-Change', 'Running-Time']
opt_result, opt_runtime = defaultdict(pd.DataFrame), defaultdict(pd.DataFrame)
for opt in machine_optimizer:
opt_result[opt] =pd.DataFrame(columns=opt_columns)
opt_result[opt] = pd.DataFrame(columns=opt_columns)
opt_result[opt].index.name = 'file'
for _, file in enumerate(os.listdir('data/')):
if file[-3:] != 'txt':
continue
partial_pcb_data, partial_component_data, feeder_data = load_data(file)
pcb_data, component_data = merge_data(partial_pcb_data, partial_component_data)
for opt in machine_optimizer:
for round_idx in range(running_round):
print(f'--- file {file}, round {round_idx}, optimizer {opt} --- ')
print(f'--- file {file}, round {round_idx + 1}, optimizer {opt} --- ')
params = parser.parse_args(['--machine_optimizer', opt, '--machine_number', str(1)])
params = parser.parse_args(['--machine_optimizer', opt, '--machine_number', str(1), '--filename', file])
start_time = time.time()
assembly_info = optimizer(pcb_data, component_data, feeder_data, params)
assembly_info = optimizer(pcb_data, component_data, feeder_data, params, hinter=False)
opt_result[opt].loc[file + str(round_idx + 1), 'Cycle'] = assembly_info[0].cycle_counter
opt_result[opt].loc[file + str(round_idx + 1), 'Pick'] = assembly_info[0].pickup_counter
@@ -140,8 +140,7 @@ def main():
for opt, result in opt_result.items():
result.to_excel(writer, sheet_name=opt, float_format='%.3f', na_rep='')
else:
# line_optimizer = ['T-Solution', 'hyper-heuristic', 'genetic', 'reconfiguration']
line_optimizer = ['genetic']
line_optimizer = ['T-Solution', 'hyper-heuristic', 'genetic', 'reconfiguration']
file_dirs = ['L01', 'L02', 'L03']
running_round = 10
@@ -169,6 +168,7 @@ def main():
warning_info = f'file: {file_dir}/{file}: an unexpected error occurs for data loader'
warnings.warn(warning_info, SyntaxWarning)
continue
machine_number = len(partial_pcb_data)
if not os.path.exists(f'record/{file_dir}'):
os.makedirs(f'record/{file_dir}')
@@ -207,11 +207,12 @@ def main():
['--filename', file_dir + '/' + file, '--machine_number', str(machine_number),
'--line_optimizer', line_opt, '--save_suffix', f'({round_idx + 1})'])
start_time = time.time()
assembly_info = optimizer(merge_pcb_data, merge_component_data, params)
assembly_info = optimizer(merge_pcb_data, merge_component_data, None, params)
line_opt_result[file_dir].loc[file, line_opt + str(round_idx + 1)] = max(
info.total_time for info in assembly_info)
line_opt_runtime[file_dir].loc[file, line_opt + str(round_idx + 1)] = time.time() - start_time
for machine_idx, info in enumerate(assembly_info):
print(
f'assembly time for machine {machine_idx + 1: d}: {info.total_time: .3f} s, '