167 lines
7.3 KiB
Python
167 lines
7.3 KiB
Python
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
|
||
|