增加预安装供料器功能、路径规划模型支持单点、整线优化支持批量处理

This commit is contained in:
2024-11-01 09:14:44 +08:00
parent 37f4e5b02c
commit 045f2f394d
15 changed files with 990 additions and 936 deletions

View File

@ -1,3 +1,4 @@
import copy
import math
from functools import reduce
@ -6,23 +7,23 @@ from base_optimizer.result_analysis import placement_info_evaluation
@timer_wrapper
def feeder_priority_assignment(component_data, pcb_data, hinter=True):
def feeder_priority_assignment(component_data, pcb_data, feeder_data, hinter=True):
feeder_allocate_val = None
component_result, cycle_result, feeder_slot_result = None, None, None
nozzle_pattern_list = feeder_nozzle_pattern(component_data)
pbar = tqdm(total=len(nozzle_pattern_list), desc='feeder priority process') if hinter else None
# 第1步确定吸嘴分配模式
for nozzle_pattern in nozzle_pattern_list:
feeder_data = pd.DataFrame(columns=['slot', 'part', 'arg'])
feeder_data_cpy = copy.deepcopy(feeder_data)
# 第2步分配供料器位置
feeder_allocate(component_data, pcb_data, feeder_data, nozzle_pattern, figure=False)
feeder_allocate(component_data, pcb_data, feeder_data_cpy, nozzle_pattern, figure=False)
# 第3步扫描供料器基座确定元件拾取的先后顺序
component_assign, cycle_assign, feeder_slot_assign = feeder_base_scan(component_data, pcb_data, feeder_data)
component_assign, cycle_assign, feeder_slot_assign = feeder_base_scan(component_data, pcb_data, feeder_data_cpy)
info = placement_info_evaluation(component_data, pcb_data, OptResult(component_assign, cycle_assign,
feeder_slot_assign), hinter=False)
val = 0.356 * info.cycle_counter + 0.949 * info.nozzle_change_counter + 0.159 * info.pickup_counter \
+ 0.002 * info.pickup_distance
val = Fit_cy * info.cycle_counter + Fit_nz * info.nozzle_change_counter + Fit_pu * info.pickup_counter\
+ Fit_mv * info.pickup_distance
if feeder_allocate_val is None or val < feeder_allocate_val:
feeder_allocate_val = val
component_result, cycle_result, feeder_slot_result = component_assign, cycle_assign, feeder_slot_assign
@ -40,10 +41,8 @@ def feeder_nozzle_pattern(component_data):
if data.points == 0:
continue
nozzle_points[data.nz] += data.points
head_assign_indexes = [int(math.ceil(max_head_index + 0.5) - 4.5 - pow(-1, h) * (math.ceil(h / 2) - 0.5)) for h in
range(1, max_head_index + 1)]
while len(nozzle_points):
nozzle_heads, nozzle_indices = defaultdict(int), defaultdict(str),
min_points_nozzle = None
@ -97,7 +96,6 @@ def feeder_nozzle_pattern(component_data):
idx += 1
nozzle_points.pop(min_points_nozzle)
return nozzle_pattern_list
@ -197,21 +195,8 @@ def feeder_allocate(component_data, pcb_data, feeder_data, nozzle_pattern, figur
if feeder_assign[idx] != -2:
continue
if len(nozzle_pattern) == 0: # 吸嘴匹配模式空,优先分配元件,根据分配元件倒推吸嘴匹配模式
nozzle_assign = ''
max_points, max_nozzle_points = 0, 0
for nozzle in set(nozzle_pattern):
if len(tmp_nozzle_component[nozzle]) == 0:
continue
part = max(tmp_nozzle_component[nozzle],
key=lambda x: tmp_feeder_points[x] / tmp_feeder_limit[x]
if tmp_feeder_points[x] != 0 else 0)
index_ = tmp_nozzle_component[nozzle].index(part)
if max_points < tmp_nozzle_component_points[nozzle][index_]:
max_points, nozzle_assign = tmp_nozzle_component_points[nozzle][index_], nozzle
else:
# 吸嘴匹配模式非空,按对应吸嘴类型进行元件分配
nozzle_assign = nozzle_pattern[idx]
# 吸嘴匹配模式空,按对应吸嘴类型进行元件分配
nozzle_assign = nozzle_pattern[idx]
if len(tmp_nozzle_component[nozzle_assign]) == 0:
# 当前头对应吸嘴类型无可用元件,将计划分配的元件压入堆栈
@ -233,6 +218,7 @@ def feeder_allocate(component_data, pcb_data, feeder_data, nozzle_pattern, figur
tmp_feeder_limit[x] != 0 else 0))
part = tmp_nozzle_component[nozzle_assign][index_]
feeder_type = component_data.loc[part].fdr
extra_width, extra_slot = feeder_width[feeder_type][0] + feeder_width[feeder_type][1] - slot_interval, 1
slot_overlap = False
@ -241,6 +227,9 @@ def feeder_allocate(component_data, pcb_data, feeder_data, nozzle_pattern, figur
if feeder_base[slot_] != -2 or slot_ > max_slot_index // 2:
slot_overlap = True
break
if idx + extra_slot // 2 < max_head_index and feeder_assign[idx + extra_slot // 2] >= 0:
slot_overlap = True
break
extra_width -= slot_interval
extra_slot += 1
@ -376,7 +365,8 @@ def feeder_allocate(component_data, pcb_data, feeder_data, nozzle_pattern, figur
best_assign = feeder_assign.copy()
best_assign_points = feeder_assign_points.copy()
best_assign_slot = slot
best_nozzle_component, best_nozzle_component_points = tmp_nozzle_component, tmp_nozzle_component_points
best_nozzle_component, best_nozzle_component_points = \
tmp_nozzle_component.copy(), tmp_nozzle_component_points.copy()
if not best_assign_points:
break
@ -384,7 +374,6 @@ def feeder_allocate(component_data, pcb_data, feeder_data, nozzle_pattern, figur
for idx, part in enumerate(best_assign):
if part < 0:
continue
# 新安装的供料器
if feeder_base[best_assign_slot + idx * interval_ratio] != part:
# 除去分配给最大化同时拾取周期的项,保留结余项
@ -419,6 +408,8 @@ def feeder_allocate(component_data, pcb_data, feeder_data, nozzle_pattern, figur
extra_slot += 1
if feeder_base[best_assign_slot + idx * interval_ratio + extra_slot] == -2:
feeder_base[best_assign_slot + idx * interval_ratio + extra_slot] = -1 # 标记槽位已占用
else:
assert 'feeder allocation conflict'
extra_width -= slot_interval
# 更新吸嘴信息
@ -441,7 +432,7 @@ def feeder_allocate(component_data, pcb_data, feeder_data, nozzle_pattern, figur
continue
part = component_data.loc[feeder].part
feeder_data.loc[len(feeder_data.index)] = [slot, part, 0]
feeder_data.loc[len(feeder_data.index)] = [slot, part]
if figure:
# 绘制供料器位置布局
@ -505,9 +496,7 @@ def feeder_base_scan(component_data, pcb_data, feeder_data):
raise ValueError(info)
component_points[idx] = data.points
component_index[data.part] = idx
if len(feeder_assign_check) != len(component_points) - component_points.count(0):
print(feeder_assign_check)
print(component_points)
assert len(feeder_assign_check) == len(component_points) - component_points.count(0) # 所有供料器均已分配槽位
mount_center_slot = defaultdict(float)
@ -562,7 +551,7 @@ def feeder_base_scan(component_data, pcb_data, feeder_data):
while True:
best_scan_part = [-1 for _ in range(max_head_index)]
best_scan_cycle = [-1 for _ in range(max_head_index)]
best_scan_cycle = [0 for _ in range(max_head_index)]
best_scan_slot = [-1 for _ in range(max_head_index)]
best_scan_nozzle_limit = copy.deepcopy(cur_nozzle_limit)
@ -619,10 +608,7 @@ def feeder_base_scan(component_data, pcb_data, feeder_data):
prev_nozzle_change = 2 * (nozzle_cycle[head] != nozzle_mode[cycle_index + 1][head])
# 避免首个周期吸杆占用率低的问题
if nozzle_cycle[head] == '':
nozzle_change = 0
else:
nozzle_change = 2 * (nozzle != nozzle_cycle[head])
nozzle_change = 2 * (nozzle != nozzle_cycle[head])
if cycle_index + 1 < len(nozzle_mode):
nozzle_change += 2 * (nozzle != nozzle_mode[cycle_index + 1][head])
@ -631,7 +617,6 @@ def feeder_base_scan(component_data, pcb_data, feeder_data):
val = e_gang_pick * gang_pick_change - e_nz_change * nozzle_change
if val < value_increment_base:
continue
component_counter += 1
scan_part[head] = part
@ -698,8 +683,6 @@ def feeder_base_scan(component_data, pcb_data, feeder_data):
# 长期收益
gang_pick_slot_dict = defaultdict(list)
for head, pick_slot in enumerate(scan_slot):
if pick_slot == -1:
continue
gang_pick_slot_dict[pick_slot - head * interval_ratio].append(scan_cycle[head])
eval_func_long_term = 0
@ -736,7 +719,6 @@ def feeder_base_scan(component_data, pcb_data, feeder_data):
if search_break:
break
scan_eval_func_list.append(scan_eval_func)
cur_scan_part = best_scan_part.copy()
@ -745,15 +727,14 @@ def feeder_base_scan(component_data, pcb_data, feeder_data):
cur_nozzle_limit = copy.deepcopy(best_scan_nozzle_limit)
if len(scan_eval_func_list) != 0:
if sum(scan_eval_func_list) > best_assigned_eval_func:
best_assigned_eval_func = sum(scan_eval_func_list)
if len(scan_eval_func_list) and sum(scan_eval_func_list) > best_assigned_eval_func:
best_assigned_eval_func = sum(scan_eval_func_list)
assigned_part = cur_scan_part.copy()
assigned_slot = cur_scan_slot.copy()
assigned_cycle = cur_scan_cycle.copy()
assigned_part = cur_scan_part.copy()
assigned_slot = cur_scan_slot.copy()
assigned_cycle = cur_scan_cycle.copy()
nozzle_insert_cycle = cycle_index
nozzle_insert_cycle = cycle_index
# 从供料器基座中移除对应数量的贴装点
nonzero_cycle = [cycle for cycle in assigned_cycle if cycle > 0]