增加预安装供料器功能、路径规划模型支持单点、整线优化支持批量处理
This commit is contained in:
@ -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]
|
||||
|
Reference in New Issue
Block a user