整线优化第一版论文定稿工程

增加了整线批量测试
修改了现有min-max模型路径
修改了遗传算法整体框架
估计器增加异常数据剔除
封装优化结果类
修改供料器扫描算法中重复吸嘴组的判定
This commit is contained in:
2024-06-26 09:44:08 +08:00
parent cbeba48da0
commit 37f4e5b02c
14 changed files with 749 additions and 669 deletions

View File

@ -1,7 +1,3 @@
import copy
import pandas as pd
from base_optimizer.optimizer_common import *
from base_optimizer.result_analysis import *
@ -10,7 +6,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)
# mdl.setParam('TimeLimit', 0.01)
nozzle_type, component_type = [], []
for _, data in component_data.iterrows():
@ -18,11 +14,6 @@ def line_optimizer_model(component_data, pcb_data, machine_num, hinter=True):
nozzle_type.append(data.nz)
component_type.append(data.part)
average_pos = 0
for _, data in pcb_data.iterrows():
average_pos += data.x
slot_start = int(round(average_pos / len(pcb_data) + stopper_pos[0] - slotf1_pos[0]) / slot_interval) + 1
ratio = 1
J = len(nozzle_type)
N = 10000
@ -31,8 +22,8 @@ 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 = len(pcb_data)
K = math.ceil(len(pcb_data) * 1.0 / H / M) + 1
# K = 3
CompOfNozzle = [[0 for _ in range(J)] for _ in range(I)] # Compatibility
component_point = [0 for _ in range(I)]
@ -43,85 +34,88 @@ 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 - 1), list_range(H), list_range(M), vtype=GRB.CONTINUOUS)
# u = mdl.addVars(list_range(K), list_range(M), vtype=GRB.INTEGER)
d_plus = mdl.addVars(list_range(J), list_range(H), list_range(K - 1), list_range(M), vtype=GRB.CONTINUOUS)
d_minus = mdl.addVars(list_range(J), list_range(H), list_range(K - 1), list_range(M), vtype=GRB.CONTINUOUS)
d = mdl.addVars(list_range(K), list_range(H), list_range(M), lb=0, vtype=GRB.CONTINUOUS)
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)
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='')
x = mdl.addVars(list_range(I), list_range(S), list_range(K), list_range(H), list_range(M), vtype=GRB.BINARY)
n = mdl.addVars(list_range(H), list_range(M), 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))
mdl.addConstrs(
quicksum(x[i, s, k, h, m] for i in range(I) for s in range(S)) <= g[k, m] for k in range(K) for h in range(H)
for m in range(M))
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] * x[i, s, k, h, m] for i in range(I) for s in range(S) for j in range(J)) <= 1 for k
in range(K) for h in range(H) for m in range(M))
# nozzle available number constraint
mdl.addConstrs(
quicksum(CompOfNozzle[i][j] * x[i, s, k, h, m] for i in range(I) for s in range(S) for h in range(H)) <= H for k
in range(K) for j in range(J) for m in range(M))
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))
# work completion
mdl.addConstrs(
quicksum(x[i, s, k, h, m] for s in range(S) for k in range(K) for h in range(H) for m in range(M)) ==
component_point[i] for i in range(I))
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] * x[i, s, k, h, m] for i in range(I) for s in range(S)) - quicksum(
CompOfNozzle[i][j] * x[i, s, k + 1, h, m] for i in range(I) for s in range(S)) == d_plus[j, h, k, m] - d_minus[
j, h, k, 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, 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
range(K - 1) 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, h, k, m] for j in range(J)) + quicksum(
d_minus[j, h, k, m] for j in range(J)) for k in range(K - 1) 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(n[h, m] == quicksum(d[k, h, m] for k in range(K - 1)) - 0.5 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))
# simultaneous pick
for s in range(-(H - 1) * ratio, S):
rng = list(range(max(0, -math.floor(s / ratio)), min(H, math.ceil((S - s) / ratio))))
for k in range(K):
mdl.addConstrs(
quicksum(x[i, s + h * ratio, k, h, m] for h in rng for i in range(I)) <= N * e[s, k, m] for m in
range(M))
quicksum(u[i, k, h, m] * v[s + h * ratio, k, h, m] for h in rng for i in range(I)) <= N * e[s, k, m] for
m in range(M))
mdl.addConstrs(
quicksum(x[i, s + h * ratio, k, h, m] for h in rng for i in range(I)) >= e[s, k, m] for m in range(M))
quicksum(u[i, k, h, m] * v[s + h * ratio, k, h, m] for h in rng for i in range(I)) >= e[s, k, m] for m
in range(M))
# pickup movement
# mdl.addConstrs(u[k, m] >= s1 * e[s1, k, m] - s2 * e[s2, k, m] for s1 in range(-(H - 1) * ratio, S) for s2 in
# range(-(H - 1) * ratio, S) for k in range(K))
# head - feeder slot relationship
mdl.addConstrs(
quicksum(v[s, k, h, m] for s in range(S)) == quicksum(u[i, k, h, m] for i in range(I)) for h in range(H) for k
in range(K) for m in range(M))
# feeder related
mdl.addConstrs(quicksum(f[s, i, m] for s in range(S) for m in range(M)) <= 1 for i in range(I))
mdl.addConstrs(quicksum(f[s, i, m] for s in range(S) for m in range(M)) <= component_data.iloc[i].fdn for i in range(I))
mdl.addConstrs(quicksum(f[s, i, m] for i in range(I)) <= 1 for s in range(S) for m in range(M))
mdl.addConstrs(
quicksum(x[i, s, k, h, m] for h in range(H) for k in range(K)) >= f[s, i, m] for i in range(I) for s in range(S)
for m in range(M))
quicksum(u[i, k, h, m] * v[s, k, h, m] for h in range(H) for k in range(K)) >= f[s, i, m] for i in range(I) for
s in range(S) for m in range(M))
mdl.addConstrs(
quicksum(x[i, s, k, h, m] for h in range(H) for k in range(K)) <= N * f[s, i, m] for i in range(I) for s in
range(S) for m in range(M))
mdl.addConstrs(
quicksum(f[s, i, m] for i in range(I)) >= quicksum(f[s + 1, i, m] for i in range(I)) for s in range(S - 1) for m
in range(M))
quicksum(u[i, k, h, m] * v[s, k, h, m] for h in range(H) for k in range(K)) <= N * f[s, i, m] for i in range(I)
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
range(-(H - 1) * ratio, S) for s2 in range(-(H - 1) * ratio, S) for k in range(K) for m in range(M))
# objective
T_cy, T_nz, T_pu, T_pl = 2, 3, 1, 1
mdl.addConstrs(obj >= T_cy * quicksum(g[k, m] for k in range(K)) + T_nz * quicksum(
d[k, h, m] for h in range(H) for k in range(K - 1)) + T_pl * quicksum(
e[s, k, m] for s in range(-(H - 1) * ratio, S) for k in range(K)) + T_pl * quicksum(
x[i, s, k, h, m] for i in range(I) for s in range(S) for k in range(K) for h in range(H)) for m in range(M))
mdl.addConstrs(obj >= Fit_cy * quicksum(g[k, m] for k in range(K)) + Fit_nz * 2 * 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))
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))}')
pcb_part_indices = defaultdict(list)
for idx, data in pcb_data.iterrows():
@ -131,42 +125,52 @@ def line_optimizer_model(component_data, pcb_data, machine_num, hinter=True):
for m in range(M):
partial_component_data, partial_pcb_data = copy.deepcopy(component_data), pd.DataFrame(columns=pcb_data.columns)
partial_component_data['points'] = 0
part_index = defaultdict(int)
for idx, data in component_data.iterrows():
part_index[data.part] = idx
component_result, cycle_result, feeder_slot_result = [], [], []
head_place_pos = []
for k in range(K):
if abs(g[k, m].x) < 1e-3:
continue
component_result.append([-1 for _ in range(H)])
cycle_result.append(1)
feeder_slot_result.append([-1 for _ in range(H)])
for h in range(H):
for i in range(I):
for s in range(S):
if abs(x[i, s, k, h, m].x) < 1e-3:
continue
if component_result[-1][h] != -1:
assert 1
component_result[-1][h] = i
feeder_slot_result[-1][h] = slot_start + s * 2
if abs(u[i, k, h, m].x) < 1e-3:
continue
component_result[-1][h] = i
idx = pcb_part_indices[component_data.iloc[i].part][0]
partial_pcb_data = pd.concat([partial_pcb_data, pd.DataFrame(pcb_data.iloc[idx]).T])
head_place_pos.append(pcb_data.iloc[idx].x - h * head_interval)
pcb_part_indices[component_data.iloc[i].part].pop(0)
partial_component_data.loc[i, 'points'] += 1
for s in range(S):
if abs(v[s, k, h, m].x) < 1e-3:
continue
feeder_slot_result[-1][h] = s
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
idx = pcb_part_indices[component_data.iloc[i].part][0]
partial_pcb_data = pd.concat([partial_pcb_data, pd.DataFrame(pcb_data.iloc[idx]).T])
pcb_part_indices[component_data.iloc[i].part].pop(0)
partial_component_data.loc[i, 'points'] += 1
print(component_result)
print(cycle_result)
print(feeder_slot_result)
placement_result, head_sequence = greedy_placement_route_generation(partial_component_data, partial_pcb_data,
component_result, cycle_result,
feeder_slot_result, hinter=False)
print('----- Placement machine ' + str(m + 1) + ' ----- ')
info = placement_info_evaluation(partial_component_data, partial_pcb_data, component_result, cycle_result,
feeder_slot_result, placement_result, head_sequence, hinter=False)
optimization_assign_result(partial_component_data, partial_pcb_data, component_result, cycle_result,
feeder_slot_result, nozzle_hinter=True, component_hinter=True, feeder_hinter=True)
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)
optimization_assign_result(partial_component_data, partial_pcb_data, opt_res, nozzle_hinter=True,
component_hinter=True, feeder_hinter=True)
info.print()
assembly_info.append(info)
print('------------------------------ ')