Files
smt-optimizer/base_optimizer/optimizer_scanbased.py

167 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from base_optimizer.optimizer_common import *
@timer_wrapper
def optimizer_genetic_scanning(component_data, pcb_data, hinter):
population_size = 200 # 种群规模
crossover_rate, mutation_rate = .4, .02
n_generation = 500
component_points = [0] * len(component_data)
for i in range(len(pcb_data)):
part = pcb_data.loc[i]['part']
part_index = component_data[component_data['part'] == part].index.tolist()[0]
component_points[part_index] += 1
nozzle_type = component_data.loc[part_index]['nz']
if nozzle_type not in nozzle_limit.keys() or nozzle_limit[nozzle_type] <= 0:
info = 'there is no available nozzle [' + nozzle_type + '] for the assembly process'
raise ValueError(info)
# randomly generate permutations
generation_ = np.array([i for i in range(max_slot_index // 2)]) # 仅考虑前基座
pop_individual, pop_val = [], []
for _ in range(population_size):
np.random.shuffle(generation_)
pop_individual.append(generation_.tolist())
_, cycle_result, feeder_slot_result = convert_individual_2_result(component_points, pop_individual[-1])
pop_val.append(feeder_arrange_evaluate(feeder_slot_result, cycle_result))
sigma_scaling(pop_val, 1)
with tqdm(total=n_generation) as pbar:
pbar.set_description('hybrid genetic process')
new_pop_val, new_pop_individual = [], []
# min-max convert
max_val = 1.5 * max(pop_val)
convert_pop_val = list(map(lambda v: max_val - v, pop_val))
for _ in range(n_generation):
# 交叉
for pop in range(population_size):
if pop % 2 == 0 and np.random.random() < crossover_rate:
index1, index2 = roulette_wheel_selection(convert_pop_val), -1
while True:
index2 = roulette_wheel_selection(convert_pop_val)
if index1 != index2:
break
# 两点交叉算子
offspring1, offspring2 = cycle_crossover(pop_individual[index1], pop_individual[index2])
# 变异
if np.random.random() < mutation_rate:
offspring1 = swap_mutation(offspring1)
if np.random.random() < mutation_rate:
offspring2 = swap_mutation(offspring2)
_, cycle_result, feeder_slot_result = convert_individual_2_result(component_points, offspring1)
new_pop_val.append(feeder_arrange_evaluate(feeder_slot_result, cycle_result))
new_pop_individual.append(offspring1)
_, cycle_result, feeder_slot_result = convert_individual_2_result(component_points, offspring2)
new_pop_val.append(feeder_arrange_evaluate(feeder_slot_result, cycle_result))
new_pop_individual.append(offspring2)
# generate next generation
top_k_index = get_top_k_value(pop_val, population_size - len(new_pop_individual), reverse=False)
for index in top_k_index:
new_pop_individual.append(pop_individual[index])
new_pop_val.append(pop_val[index])
pop_individual, pop_val = new_pop_individual, new_pop_val
sigma_scaling(pop_val, 1)
# select the best individual
pop = np.argmin(pop_val)
component_result, cycle_result, feeder_slot_result = convert_individual_2_result(component_points, pop_individual[pop])
placement_result, head_sequence = greedy_placement_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
def convert_individual_2_result(component_points, pop):
component_result, cycle_result, feeder_slot_result = [], [], []
feeder_part = [-1] * (max_slot_index // 2) # 已安装在供料器基座上的元件0: 未分配)
feeder_base_points = [0] * (max_slot_index // 2) # 供料器基座结余贴装点数量
# 将基因信息转换为供料器基座安装结果
for idx, gene in enumerate(pop):
if idx >= len(component_points):
break
feeder_part[gene], feeder_base_points[gene] = idx, component_points[idx]
# TODO: 暂时未考虑可用吸嘴数的限制
while True:
# === 周期内循环 ===
assigned_part = [-1 for _ in range(max_head_index)] # 当前扫描到的头分配元件信息
assigned_slot = [-1 for _ in range(max_head_index)] # 当前扫描到的供料器分配信息
prev_scan_slot = len(feeder_part) // 2 # 前一轮扫描的位置
while True:
best_scan_part, best_scan_slot = [-1 for _ in range(max_head_index)], [-1 for _ in range(max_head_index)]
best_slot_index = -1
for slot in range(max_slot_index // 2 - (max_head_index - 1) * interval_ratio):
scan_part, scan_slot = assigned_part.copy(), assigned_slot.copy()
for head in range(max_head_index):
part = feeder_part[slot + head * interval_ratio]
# 贴装头和拾取槽位满足对应关系
if scan_part[head] == -1 and part != -1 and feeder_base_points[slot + head * interval_ratio] > 0:
scan_part[head], scan_slot[head] = part, slot + head * interval_ratio + 1
if scan_part.count(-1) < best_scan_part.count(-1) or (scan_part.count(-1) == best_scan_part.count(-1)
and abs(slot - prev_scan_slot) <
abs(best_slot_index - prev_scan_slot)):
best_slot_index = slot
best_scan_part, best_scan_slot = scan_part.copy(), scan_slot.copy()
assigned_points = 0
for idx, slot in enumerate(best_scan_slot):
if slot != -1 and assigned_slot[idx] == -1:
feeder_base_points[slot - 1] -= 1
assigned_points += 1
assigned_part, assigned_slot = best_scan_part.copy(), best_scan_slot.copy()
prev_scan_slot = best_slot_index
if assigned_part.count(-1) == 0 or assigned_points == 0:
break
if len(cycle_result) == 0 or component_result[-1] != assigned_part:
cycle_result.append(1)
component_result.append(assigned_part)
feeder_slot_result.append(assigned_slot)
else:
cycle_result[-1] += 1
if sum(feeder_base_points) == 0:
break
return component_result, cycle_result, feeder_slot_result
def feeder_arrange_evaluate(feeder_slot_result, cycle_result):
assert len(feeder_slot_result) == len(cycle_result)
arrange_val = 0
for cycle, feeder_slot in enumerate(feeder_slot_result):
pick_slot = set()
for head, slot in enumerate(feeder_slot):
pick_slot.add(slot - head * interval_ratio)
arrange_val += len(pick_slot) * t_pick * cycle_result[cycle]
pick_slot = list(pick_slot)
pick_slot.sort()
arrange_val += axis_moving_time(pick_slot[0] - pick_slot[-1]) * cycle_result[cycle]
return arrange_val