增加预安装供料器功能、路径规划模型支持单点、整线优化支持批量处理
This commit is contained in:
@ -7,8 +7,10 @@ def list_range(start, end=None):
|
||||
|
||||
@timer_wrapper
|
||||
def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, partition=True, initial=False, hinter=True):
|
||||
|
||||
# data preparation: convert data to index
|
||||
component_list, nozzle_list = defaultdict(int), defaultdict(int)
|
||||
component_feeder = defaultdict(int)
|
||||
cpidx_2_part, nzidx_2_nozzle, cpidx_2_nzidx = {}, {}, {}
|
||||
arg_slot_rng = None if len(feeder_data) == 0 else [feeder_data.iloc[0].slot, feeder_data.iloc[-1].slot]
|
||||
for idx, data in component_data.iterrows():
|
||||
@ -21,6 +23,7 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
|
||||
nzidx_2_nozzle[nz_idx] = nozzle
|
||||
|
||||
component_list[part] = 0
|
||||
component_feeder[part] = data.fdn
|
||||
cpidx_2_nzidx[idx] = nz_idx
|
||||
|
||||
for _, data in pcb_data.iterrows():
|
||||
@ -40,13 +43,7 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
|
||||
assert idx != -1
|
||||
part_feederbase[idx] = data.slot # part index - slot
|
||||
|
||||
if not reduction:
|
||||
ratio = 2 # 直接导入飞达数据时,采用正常吸杆间隔
|
||||
else:
|
||||
if len(component_list) <= 1.5 * max_head_index:
|
||||
ratio = 1
|
||||
else:
|
||||
ratio = 2
|
||||
ratio = 1 if reduction else 2
|
||||
I, J = len(cpidx_2_part.keys()), len(nzidx_2_nozzle.keys())
|
||||
# === determine the hyper-parameter of L ===
|
||||
# first phase: calculate the number of heads for each type of nozzle
|
||||
@ -80,13 +77,12 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
|
||||
pre_objbst, pre_changetime = None, None
|
||||
def terminate_condition(mdl, where):
|
||||
if where == GRB.Callback.MIP:
|
||||
objbst = mdl.cbGet(GRB.Callback.MIP_OBJBST)
|
||||
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
|
||||
if pre_objbst and abs(pre_objbst - objbst) < 1e-3:
|
||||
if pre_changetime and changetime - pre_changetime > 45:
|
||||
# pass
|
||||
if pre_changetime and changetime - pre_changetime > 100 * (1 - objbnd / objbst):
|
||||
mdl.terminate()
|
||||
else:
|
||||
pre_changetime = changetime
|
||||
@ -150,10 +146,8 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
|
||||
break
|
||||
level += 1
|
||||
|
||||
weight_cycle, weight_nz_change, weight_pick = 2, 3, 2
|
||||
|
||||
L = len(cycle_assignment) if partition else len(pcb_data)
|
||||
S = ratio * I 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()) * 2 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):
|
||||
@ -247,8 +241,8 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
|
||||
v[part_feederbase[i], h, idx].Start = 1
|
||||
|
||||
# === Objective ===
|
||||
mdl.setObjective(weight_cycle * quicksum(WL[l] for l in range(L)) + weight_nz_change * quicksum(
|
||||
NC[h] for h in range(max_head_index)) + weight_pick * quicksum(
|
||||
mdl.setObjective(Fit_cy * quicksum(WL[l] for l in range(L)) + 2 * Fit_nz * quicksum(
|
||||
NC[h] for h in range(max_head_index)) + Fit_pu * quicksum(
|
||||
PU[s, l] for s in range(-(max_head_index - 1) * ratio, S) for l in range(L)))
|
||||
|
||||
# === Constraint ===
|
||||
@ -256,13 +250,13 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
|
||||
mdl.addConstrs(WL[l] <= 1 for l in range(L))
|
||||
|
||||
# work completion
|
||||
# mdl.addConstrs(c[i, h, l] == WL[l] * y[i, h, l] for i in range(I) for h in range(max_head_index) for l in range(L))
|
||||
mdl.addConstrs(
|
||||
c[i, h, l] <= max_cycle * y[i, h, l] for i in range(I) for h in range(max_head_index) for l in range(L))
|
||||
mdl.addConstrs(c[i, h, l] <= WL[l] for i in range(I) for h in range(max_head_index) for l in range(L))
|
||||
mdl.addConstrs(
|
||||
c[i, h, l] >= WL[l] - max_cycle * (1 - y[i, h, l]) for i in range(I) for h in range(max_head_index) for l in
|
||||
range(L))
|
||||
mdl.addConstrs(c[i, h, l] == WL[l] * y[i, h, l] for i in range(I) for h in range(max_head_index) for l in range(L))
|
||||
# mdl.addConstrs(
|
||||
# c[i, h, l] <= max_cycle * y[i, h, l] for i in range(I) for h in range(max_head_index) for l in range(L))
|
||||
# mdl.addConstrs(c[i, h, l] <= WL[l] for i in range(I) for h in range(max_head_index) for l in range(L))
|
||||
# mdl.addConstrs(
|
||||
# c[i, h, l] >= WL[l] - max_cycle * (1 - y[i, h, l]) for i in range(I) for h in range(max_head_index) for l in
|
||||
# range(L))
|
||||
|
||||
mdl.addConstrs(
|
||||
quicksum(c[i, h, l] for h in range(max_head_index) for l in range(L)) == component_list[cpidx_2_part[i]] for i
|
||||
@ -278,12 +272,12 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
|
||||
mdl.addConstr(quicksum(v[s + h * ratio, h, l] for h in rng) <= max_head_index * p[s, l])
|
||||
mdl.addConstr(quicksum(v[s + h * ratio, h, l] for h in rng) >= p[s, l])
|
||||
|
||||
# mdl.addConstrs(PU[s, l] == p[s, l] * WL[l] for s in range(-(max_head_index - 1) * ratio, S) for l in range(L))
|
||||
mdl.addConstrs(PU[s, l] <= max_cycle * p[s, l] for s in range(-(max_head_index - 1) * ratio, S) for l in range(L))
|
||||
mdl.addConstrs(PU[s, l] <= WL[l] for s in range(-(max_head_index - 1) * ratio, S) for l in range(L))
|
||||
mdl.addConstrs(
|
||||
PU[s, l] >= WL[l] - max_cycle * (1 - p[s, l]) for s in range(-(max_head_index - 1) * ratio, S) for l in
|
||||
range(L))
|
||||
mdl.addConstrs(PU[s, l] == p[s, l] * WL[l] for s in range(-(max_head_index - 1) * ratio, S) for l in range(L))
|
||||
# mdl.addConstrs(PU[s, l] <= max_cycle * p[s, l] for s in range(-(max_head_index - 1) * ratio, S) for l in range(L))
|
||||
# mdl.addConstrs(PU[s, l] <= WL[l] for s in range(-(max_head_index - 1) * ratio, S) for l in range(L))
|
||||
# mdl.addConstrs(
|
||||
# PU[s, l] >= WL[l] - max_cycle * (1 - p[s, l]) for s in range(-(max_head_index - 1) * ratio, S) for l in
|
||||
# range(L))
|
||||
|
||||
# nozzle change
|
||||
mdl.addConstrs(
|
||||
@ -307,7 +301,7 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
|
||||
for l in range(L))
|
||||
|
||||
# available number of feeder
|
||||
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_feeder[cpidx_2_part[i]] for i in range(I))
|
||||
|
||||
# available number of nozzle
|
||||
mdl.addConstrs(quicksum(z[j, h, l] for h in range(max_head_index)) <= max_head_index for j in range(J) for l in range(L))
|
||||
@ -320,19 +314,19 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
|
||||
|
||||
# others
|
||||
mdl.addConstrs(quicksum(z[j, h, l] for j in range(J)) <= 1 for h in range(max_head_index) for l in range(L))
|
||||
# mdl.addConstrs(
|
||||
# quicksum(x[i, s, h, l] for h in range(max_head_index) for l in range(L)) >= f[s, i] for i in range(I)
|
||||
# for s in range(S))
|
||||
# mdl.addConstrs(
|
||||
# quicksum(x[i, s, h, l] for h in range(max_head_index) for l in range(L)) <= M * f[s, i] for i in
|
||||
# range(I) for s in range(S))
|
||||
|
||||
mdl.addConstrs(
|
||||
f[s, i] >= x[i, s, h, l] for s in range(S) for i in range(I) for h in range(max_head_index) for l in range(L))
|
||||
|
||||
quicksum(x[i, s, h, l] for h in range(max_head_index) for l in range(L)) >= f[s, i] for i in range(I)
|
||||
for s in range(S))
|
||||
mdl.addConstrs(
|
||||
quicksum(x[i, s, h, l] for h in range(max_head_index) for l in range(L)) >= f[s, i] for s in
|
||||
range(S) for i in range(I))
|
||||
quicksum(x[i, s, h, l] for h in range(max_head_index) for l in range(L)) <= M * f[s, i] for i in
|
||||
range(I) for s in range(S))
|
||||
|
||||
# mdl.addConstrs(
|
||||
# f[s, i] >= x[i, s, h, l] for s in range(S) for i in range(I) for h in range(max_head_index) for l in range(L))
|
||||
#
|
||||
# mdl.addConstrs(
|
||||
# quicksum(x[i, s, h, l] for h in range(max_head_index) for l in range(L)) >= f[s, i] for s in
|
||||
# range(S) for i in range(I))
|
||||
|
||||
# the constraints to speed up the search process
|
||||
mdl.addConstrs(
|
||||
@ -340,13 +334,13 @@ 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.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(WL[l] >= WL[l + 1] for l in range(L - 1))
|
||||
# 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.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))
|
||||
|
||||
@ -368,6 +362,7 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
|
||||
print('num of constrs: ', str(len(mdl.getConstrs())), ', num of vars: ', str(len(mdl.getVars())))
|
||||
|
||||
mdl.optimize(terminate_condition)
|
||||
# mdl.optimize()
|
||||
|
||||
# === result generation ===
|
||||
nozzle_assign, component_assign = [], []
|
||||
@ -505,348 +500,5 @@ def gurobi_optimizer(pcb_data, component_data, feeder_data, reduction=True, part
|
||||
print('component assignment: ', component_assign)
|
||||
print('feeder assignment: ', feeder_assign)
|
||||
print('cycle assignment: ', cycle_assign)
|
||||
|
||||
return component_assign, feeder_assign, cycle_assign
|
||||
|
||||
|
||||
def scan_based_placement_route_generation(component_data, pcb_data, component_assign, cycle_assign):
|
||||
placement_result, head_sequence_result = [], []
|
||||
|
||||
mount_point_pos, mount_point_index, mount_point_angle, mount_point_part = [], [], [], []
|
||||
for i, data in pcb_data.iterrows():
|
||||
component_index = component_data[component_data.part == data.part].index.tolist()[0]
|
||||
# 记录贴装点序号索引和对应的位置坐标
|
||||
mount_point_index.append(i)
|
||||
mount_point_pos.append([data.x + stopper_pos[0], data.y + stopper_pos[1]])
|
||||
mount_point_angle.append(data.r)
|
||||
|
||||
mount_point_part.append(component_index)
|
||||
|
||||
lBoundary, rBoundary = min(mount_point_pos, key=lambda x: x[0])[0], max(mount_point_pos, key=lambda x: x[0])[0]
|
||||
search_step = max((rBoundary - lBoundary) / max_head_index / 2, 0)
|
||||
|
||||
ref_pos_y = min(mount_point_pos, key=lambda x: x[1])[1]
|
||||
for cycle_index, component_cycle in enumerate(component_assign):
|
||||
for _ in range(cycle_assign[cycle_index]):
|
||||
min_dist = None
|
||||
tmp_assigned_placement, tmp_assigned_head_seq = [], []
|
||||
tmp_mount_point_pos, tmp_mount_point_index = [], []
|
||||
for search_dir in range(3): # 不同的搜索方向,贴装头和起始点的选取方法各不相同
|
||||
if search_dir == 0:
|
||||
# 从左向右搜索
|
||||
searchPoints = np.arange(lBoundary, (lBoundary + rBoundary) / 2, search_step)
|
||||
head_range = list(range(max_head_index))
|
||||
elif search_dir == 1:
|
||||
# 从右向左搜索
|
||||
searchPoints = np.arange(rBoundary + 1e-3, (lBoundary + rBoundary) / 2, -search_step)
|
||||
head_range = list(range(max_head_index - 1, -1, -1))
|
||||
else:
|
||||
# 从中间向两边搜索
|
||||
searchPoints = np.arange(lBoundary, rBoundary, search_step / 2)
|
||||
head_range, head_index = [], (max_head_index - 1) // 2
|
||||
while head_index >= 0:
|
||||
if 2 * head_index != max_head_index - 1:
|
||||
head_range.append(max_head_index - 1 - head_index)
|
||||
head_range.append(head_index)
|
||||
head_index -= 1
|
||||
|
||||
for startPoint in searchPoints:
|
||||
mount_point_pos_cpy, mount_point_index_cpy = copy.deepcopy(mount_point_pos), copy.deepcopy(
|
||||
mount_point_index)
|
||||
mount_point_angle_cpy = copy.deepcopy(mount_point_angle)
|
||||
|
||||
assigned_placement = [-1] * max_head_index
|
||||
assigned_mount_point = [[0, 0]] * max_head_index
|
||||
assigned_mount_angle = [0] * max_head_index
|
||||
head_counter, point_index = 0, -1
|
||||
for head_index in head_range:
|
||||
if head_counter == 0:
|
||||
component_index = component_assign[cycle_index][head_index]
|
||||
|
||||
if component_index == -1:
|
||||
continue
|
||||
|
||||
min_horizontal_distance = None
|
||||
for index, mount_index in enumerate(mount_point_index_cpy):
|
||||
if mount_point_part[mount_index] != component_index:
|
||||
continue
|
||||
horizontal_distance = abs(mount_point_pos_cpy[index][0] - startPoint) + 1e-3 * abs(
|
||||
mount_point_pos_cpy[index][1] - ref_pos_y)
|
||||
|
||||
if min_horizontal_distance is None or horizontal_distance < min_horizontal_distance:
|
||||
min_horizontal_distance = horizontal_distance
|
||||
point_index = index
|
||||
else:
|
||||
point_index = -1
|
||||
min_cheby_distance = None
|
||||
|
||||
next_comp_index = component_assign[cycle_index][head_index]
|
||||
if assigned_placement[head_index] != -1 or next_comp_index == -1:
|
||||
continue
|
||||
for index, mount_index in enumerate(mount_point_index_cpy):
|
||||
if mount_point_part[mount_index] != next_comp_index:
|
||||
continue
|
||||
|
||||
point_pos = [[mount_point_pos_cpy[index][0] - head_index * head_interval,
|
||||
mount_point_pos_cpy[index][1]]]
|
||||
|
||||
cheby_distance, euler_distance = 0, 0
|
||||
for next_head in range(max_head_index):
|
||||
if assigned_placement[next_head] == -1:
|
||||
continue
|
||||
point_pos.append(assigned_mount_point[next_head].copy())
|
||||
point_pos[-1][0] -= next_head * head_interval
|
||||
|
||||
point_pos = sorted(point_pos, key=lambda x: x[0])
|
||||
for mount_seq in range(len(point_pos) - 1):
|
||||
cheby_distance += max(abs(point_pos[mount_seq][0] - point_pos[mount_seq + 1][0]),
|
||||
abs(point_pos[mount_seq][1] - point_pos[mount_seq + 1][1]))
|
||||
euler_distance += math.sqrt(
|
||||
(point_pos[mount_seq][0] - point_pos[mount_seq + 1][0]) ** 2 + (
|
||||
point_pos[mount_seq][1] - point_pos[mount_seq + 1][1]) ** 2)
|
||||
|
||||
cheby_distance += 0.01 * euler_distance
|
||||
if min_cheby_distance is None or cheby_distance < min_cheby_distance:
|
||||
min_cheby_distance, min_euler_distance = cheby_distance, euler_distance
|
||||
point_index = index
|
||||
|
||||
if point_index == -1:
|
||||
continue
|
||||
|
||||
head_counter += 1
|
||||
|
||||
assigned_placement[head_index] = mount_point_index_cpy[point_index]
|
||||
assigned_mount_point[head_index] = mount_point_pos_cpy[point_index].copy()
|
||||
assigned_mount_angle[head_index] = mount_point_angle_cpy[point_index]
|
||||
|
||||
mount_point_index_cpy.pop(point_index)
|
||||
mount_point_pos_cpy.pop(point_index)
|
||||
mount_point_angle_cpy.pop(point_index)
|
||||
|
||||
dist, head_seq = dynamic_programming_cycle_path(assigned_placement, assigned_mount_point,
|
||||
assigned_mount_angle)
|
||||
|
||||
if min_dist is None or dist < min_dist:
|
||||
tmp_mount_point_pos, tmp_mount_point_index = mount_point_pos_cpy, mount_point_index_cpy
|
||||
tmp_assigned_placement, tmp_assigned_head_seq = assigned_placement, head_seq
|
||||
min_dist = dist
|
||||
|
||||
mount_point_pos, mount_point_index = tmp_mount_point_pos, tmp_mount_point_index
|
||||
|
||||
placement_result.append(tmp_assigned_placement)
|
||||
head_sequence_result.append(tmp_assigned_head_seq)
|
||||
|
||||
return placement_result, head_sequence_result
|
||||
# return placement_route_relink_heuristic(component_data, pcb_data, placement_result, head_sequence_result)
|
||||
|
||||
|
||||
def placement_route_relink_heuristic(component_data, pcb_data, placement_result, head_sequence_result, hinter=True):
|
||||
mount_point_pos, mount_point_angle, mount_point_index, mount_point_part = [], [], [], []
|
||||
for i, data in pcb_data.iterrows():
|
||||
component_index = component_data[component_data.part == data.part].index.tolist()[0]
|
||||
# 记录贴装点序号索引和对应的位置坐标
|
||||
mount_point_index.append(i)
|
||||
mount_point_pos.append([data.x + stopper_pos[0], data.y + stopper_pos[1]])
|
||||
mount_point_angle.append(data.r)
|
||||
|
||||
mount_point_part.append(component_index)
|
||||
|
||||
cycle_length, cycle_average_pos = [], []
|
||||
for cycle, placement in enumerate(placement_result):
|
||||
prev_pos, prev_angle = None, None
|
||||
cycle_pos_list = []
|
||||
cycle_length.append(0)
|
||||
for idx, head in enumerate(head_sequence_result[cycle]):
|
||||
point_index = placement[head]
|
||||
if point_index == -1:
|
||||
continue
|
||||
pos = [mount_point_pos[point_index][0] - head * head_interval, mount_point_pos[point_index][1]]
|
||||
angle = mount_point_angle[point_index]
|
||||
cycle_pos_list.append(pos)
|
||||
if prev_pos is not None:
|
||||
if head_sequence_result[cycle][idx - 1] // 2 == head_sequence_result[cycle][idx] // 2: # 同轴
|
||||
rotary_angle = prev_angle - angle
|
||||
else:
|
||||
rotary_angle = 0
|
||||
|
||||
cycle_length[-1] += max(axis_moving_time(prev_pos[0] - pos[0], 0),
|
||||
axis_moving_time(prev_pos[1] - pos[1], 1), head_rotary_time(rotary_angle))
|
||||
prev_pos, prev_angle = pos, angle
|
||||
|
||||
cycle_average_pos.append([sum(map(lambda pos: pos[0], cycle_pos_list)) / len(cycle_pos_list),
|
||||
sum(map(lambda pos: pos[1], cycle_pos_list)) / len(cycle_pos_list)])
|
||||
|
||||
best_placement_result, best_head_sequence_result = copy.deepcopy(placement_result), copy.deepcopy(
|
||||
head_sequence_result)
|
||||
|
||||
best_cycle_length, best_cycle_average_pos = copy.deepcopy(cycle_length), copy.deepcopy(cycle_average_pos)
|
||||
|
||||
n_runningtime, n_iteration = 10, 0
|
||||
start_time = time.time()
|
||||
with tqdm(total=n_runningtime, leave=False) as pbar:
|
||||
pbar.set_description('swap heuristic process')
|
||||
prev_time = start_time
|
||||
while True:
|
||||
n_iteration += 1
|
||||
|
||||
placement_result, head_sequence_result = copy.deepcopy(best_placement_result), copy.deepcopy(
|
||||
best_head_sequence_result)
|
||||
cycle_length = best_cycle_length.copy()
|
||||
cycle_average_pos = copy.deepcopy(best_cycle_average_pos)
|
||||
|
||||
cycle_index = roulette_wheel_selection(cycle_length) # 根据周期加权移动距离随机选择周期
|
||||
|
||||
point_dist = [] # 周期内各贴装点距离中心位置的切氏距离
|
||||
for head in head_sequence_result[cycle_index]:
|
||||
point_index = placement_result[cycle_index][head]
|
||||
_delta_x = abs(mount_point_pos[point_index][0] - head * head_interval - cycle_average_pos[cycle_index][0])
|
||||
_delta_y = abs(mount_point_pos[point_index][1] - cycle_average_pos[cycle_index][1])
|
||||
point_dist.append(max(_delta_x, _delta_y))
|
||||
|
||||
# 随机选择一个异常点
|
||||
head_index = head_sequence_result[cycle_index][roulette_wheel_selection(point_dist)]
|
||||
point_index = placement_result[cycle_index][head_index]
|
||||
|
||||
# 找距离该异常点最近的周期
|
||||
min_dist = None
|
||||
chg_cycle_index = -1
|
||||
for idx in range(len(cycle_average_pos)):
|
||||
if idx == cycle_index:
|
||||
continue
|
||||
dist_ = 0
|
||||
component_type_check = False
|
||||
for head in head_sequence_result[idx]:
|
||||
dist_ += max(abs(mount_point_pos[placement_result[idx][head]][0] - mount_point_pos[point_index][0]),
|
||||
abs(mount_point_pos[placement_result[idx][head]][1] - mount_point_pos[point_index][1]))
|
||||
if mount_point_part[placement_result[idx][head]] == mount_point_part[point_index]:
|
||||
component_type_check = True
|
||||
|
||||
if (min_dist is None or dist_ < min_dist) and component_type_check:
|
||||
min_dist = dist_
|
||||
chg_cycle_index = idx
|
||||
|
||||
assert chg_cycle_index != -1
|
||||
|
||||
chg_head, min_chg_dist = None, None
|
||||
chg_cycle_point = []
|
||||
for head in head_sequence_result[chg_cycle_index]:
|
||||
index = placement_result[chg_cycle_index][head]
|
||||
chg_cycle_point.append([mount_point_pos[index][0] - head * head_interval, mount_point_pos[index][1]])
|
||||
|
||||
for idx, head in enumerate(head_sequence_result[chg_cycle_index]):
|
||||
chg_cycle_point_cpy = copy.deepcopy(chg_cycle_point)
|
||||
index = placement_result[chg_cycle_index][head]
|
||||
if mount_point_part[index] != mount_point_part[point_index]:
|
||||
continue
|
||||
chg_cycle_point_cpy[idx][0] = (mount_point_pos[index][0]) - head * head_interval
|
||||
|
||||
chg_dist = 0
|
||||
aver_chg_pos = [sum(map(lambda x: x[0], chg_cycle_point_cpy)) / len(chg_cycle_point_cpy),
|
||||
sum(map(lambda x: x[1], chg_cycle_point_cpy)) / len(chg_cycle_point_cpy)]
|
||||
|
||||
for pos in chg_cycle_point_cpy:
|
||||
chg_dist += max(abs(aver_chg_pos[0] - pos[0]), abs(aver_chg_pos[1] - pos[1]))
|
||||
|
||||
# 更换后各点距离中心更近
|
||||
if min_chg_dist is None or chg_dist < min_chg_dist:
|
||||
chg_head = head
|
||||
min_chg_dist = chg_dist
|
||||
|
||||
assert chg_head is not None
|
||||
|
||||
# === 第一轮,变更周期chg_cycle_index的贴装点重排 ===
|
||||
chg_placement_res = placement_result[chg_cycle_index].copy()
|
||||
chg_placement_res[chg_head] = point_index
|
||||
|
||||
cycle_point_list = defaultdict(list)
|
||||
for head, point in enumerate(chg_placement_res):
|
||||
if point == -1:
|
||||
continue
|
||||
cycle_point_list[mount_point_part[point]].append(point)
|
||||
|
||||
for key, point_list in cycle_point_list.items():
|
||||
cycle_point_list[key] = sorted(point_list, key=lambda p: mount_point_pos[p][0])
|
||||
|
||||
chg_placement_res, chg_point_assign_res = [], [[0, 0]] * max_head_index
|
||||
chg_angle_res = [0] * max_head_index
|
||||
for head, point_index in enumerate(placement_result[chg_cycle_index]):
|
||||
if point_index == -1:
|
||||
chg_placement_res.append(-1)
|
||||
else:
|
||||
part = mount_point_part[point_index]
|
||||
chg_placement_res.append(cycle_point_list[part][0])
|
||||
chg_point_assign_res[head] = mount_point_pos[cycle_point_list[part][0]].copy()
|
||||
chg_angle_res[head] = mount_point_angle[cycle_point_list[part][0]]
|
||||
cycle_point_list[part].pop(0)
|
||||
|
||||
chg_place_moving, chg_head_res = dynamic_programming_cycle_path(chg_placement_res, chg_point_assign_res, chg_angle_res)
|
||||
|
||||
# === 第二轮,原始周期cycle_index的贴装点重排 ===
|
||||
placement_res = placement_result[cycle_index].copy()
|
||||
placement_res[head_index] = placement_result[chg_cycle_index][chg_head]
|
||||
|
||||
for point in placement_res:
|
||||
if point == -1:
|
||||
continue
|
||||
cycle_point_list[mount_point_part[point]].append(point)
|
||||
|
||||
for key, point_list in cycle_point_list.items():
|
||||
cycle_point_list[key] = sorted(point_list, key=lambda p: mount_point_pos[p][0])
|
||||
|
||||
placement_res, point_assign_res = [], [[0, 0]] * max_head_index
|
||||
angle_assign_res = [0] * max_head_index
|
||||
for head, point_index in enumerate(placement_result[cycle_index]):
|
||||
if point_index == -1:
|
||||
placement_res.append(-1)
|
||||
else:
|
||||
part = mount_point_part[point_index]
|
||||
placement_res.append(cycle_point_list[part][0])
|
||||
point_assign_res[head] = mount_point_pos[cycle_point_list[part][0]].copy()
|
||||
angle_assign_res[head] = mount_point_angle[cycle_point_list[part][0]]
|
||||
cycle_point_list[part].pop(0)
|
||||
|
||||
place_moving, place_head_res = dynamic_programming_cycle_path(placement_res, point_assign_res, angle_assign_res)
|
||||
|
||||
# 更新贴装顺序分配结果
|
||||
placement_result[cycle_index], head_sequence_result[cycle_index] = placement_res, place_head_res
|
||||
placement_result[chg_cycle_index], head_sequence_result[chg_cycle_index] = chg_placement_res, chg_head_res
|
||||
|
||||
# 更新移动路径
|
||||
cycle_length[cycle_index], cycle_length[chg_cycle_index] = place_moving, chg_place_moving
|
||||
|
||||
# 更新平均坐标和最大偏离点索引
|
||||
point_list, point_index_list = [], []
|
||||
for head in head_sequence_result[cycle_index]:
|
||||
point_index_list.append(placement_result[cycle_index][head])
|
||||
point_pos = mount_point_pos[point_index_list[-1]].copy()
|
||||
point_pos[0] -= head * head_interval
|
||||
point_list.append(point_pos)
|
||||
|
||||
cycle_average_pos[cycle_index] = [sum(map(lambda x: x[0], point_list)) / len(point_list),
|
||||
sum(map(lambda x: x[1], point_list)) / len(point_list)]
|
||||
|
||||
point_list, point_index_list = [], []
|
||||
for head in head_sequence_result[chg_cycle_index]:
|
||||
point_index_list.append(placement_result[chg_cycle_index][head])
|
||||
point_pos = mount_point_pos[point_index_list[-1]].copy()
|
||||
point_pos[0] -= head * head_interval
|
||||
point_list.append(point_pos)
|
||||
|
||||
cycle_average_pos[chg_cycle_index] = [sum(map(lambda x: x[0], point_list)) / len(point_list),
|
||||
sum(map(lambda x: x[1], point_list)) / len(point_list)]
|
||||
|
||||
if sum(cycle_length) < sum(best_cycle_length):
|
||||
best_cycle_length = cycle_length.copy()
|
||||
best_cycle_average_pos = copy.deepcopy(cycle_average_pos)
|
||||
best_placement_result, best_head_sequence_result = copy.deepcopy(placement_result), copy.deepcopy(
|
||||
head_sequence_result)
|
||||
|
||||
cur_time = time.time()
|
||||
if cur_time - start_time > n_runningtime:
|
||||
break
|
||||
|
||||
pbar.update(cur_time - prev_time)
|
||||
prev_time = cur_time
|
||||
|
||||
# print("number of iteration: ", n_iteration)
|
||||
return best_placement_result, best_head_sequence_result
|
Reference in New Issue
Block a user