优化器类的定义和实现

This commit is contained in:
2025-11-14 11:34:48 +08:00
parent a37ee38369
commit 79b09b2578
11 changed files with 4004 additions and 0 deletions

279
opt/utils.py Normal file
View File

@@ -0,0 +1,279 @@
from core.common import *
from data.type import *
head_rotary_velocity = 8e-5 # <20><>װͷR<CDB7><52><EFBFBD><EFBFBD>תʱ<D7AA><CAB1>
x_max_velocity, y_max_velocity = 1.4, 1.2
x_max_acceleration, y_max_acceleration = x_max_velocity / 0.079, y_max_velocity / 0.079
def axis_moving_time(distance, axis=0):
assert 0 <= axis <= 2
distance = abs(distance) * 1e-3
Lamax = x_max_velocity ** 2 / x_max_acceleration if axis == 0 else y_max_velocity ** 2 / y_max_acceleration
Tmax = x_max_velocity / x_max_acceleration if axis == 0 else y_max_velocity / y_max_acceleration
if axis == 0:
return 2 * math.sqrt(distance / x_max_acceleration) if distance < Lamax else 2 * Tmax + (
distance - Lamax) / x_max_velocity
elif axis == 1:
return 2 * math.sqrt(distance / y_max_acceleration) if distance < Lamax else 2 * Tmax + (
distance - Lamax) / y_max_velocity
elif axis == 2:
return abs((distance + 180) % 360 - 180) * head_rotary_velocity
return .0
def optimizer_result_hinter(config, part_data, opt_res, nozzle_hinter=False, part_hinter=False,
slot_hinter=False, place_hinter=False):
if nozzle_hinter:
columns = ['H{}'.format(i + 1) for i in range(config.head_num)] + ['cycle']
nozzle_assign = pd.DataFrame(columns=columns)
for cycle, components in enumerate(opt_res.part):
nozzle_assign_row = len(nozzle_assign)
nozzle_assign.loc[nozzle_assign_row, 'cycle'] = opt_res.cycle[cycle]
for head in range(config.head_num):
index = opt_res.part[cycle][head]
if index == -1:
nozzle_assign.loc[nozzle_assign_row, 'H{}'.format(head + 1)] = ''
else:
nozzle = part_data.loc[index]['nz']
nozzle_assign.loc[nozzle_assign_row, 'H{}'.format(head + 1)] = nozzle
for head in range(config.head_num):
if nozzle_assign_row == 0 or nozzle_assign.loc[nozzle_assign_row - 1, 'H{}'.format(head + 1)] != \
nozzle_assign.loc[nozzle_assign_row, 'H{}'.format(head + 1)]:
break
else:
nozzle_assign.loc[nozzle_assign_row - 1, 'cycle'] += nozzle_assign.loc[nozzle_assign_row, 'cycle']
nozzle_assign.drop([len(nozzle_assign) - 1], inplace=True)
print(nozzle_assign)
print('')
if part_hinter:
columns = ['H{}'.format(i + 1) for i in range(config.head_num)] + ['cycle']
part_assign = pd.DataFrame(columns=columns)
for cycle, components in enumerate(opt_res.part):
part_assign.loc[cycle, 'cycle'] = opt_res.cycle[cycle]
for head in range(config.head_num):
index = opt_res.part[cycle][head]
if index == -1:
part_assign.loc[cycle, 'H{}'.format(head + 1)] = ''
else:
# component_assign.loc[cycle, 'H{}'.format(head + 1)] = component_data.loc[index]['part']
part_assign.loc[cycle, 'H{}'.format(head + 1)] = 'C' + str(index)
print(part_assign)
print('')
if slot_hinter:
columns = ['H{}'.format(i + 1) for i in range(config.head_num)] + ['cycle']
slot_assign = pd.DataFrame(columns=columns)
for cycle, components in enumerate(opt_res.slot):
slot_assign.loc[cycle, 'cycle'] = opt_res.cycle[cycle]
for head in range(config.head_num):
slot = opt_res.slot[cycle][head]
if slot == -1:
slot_assign.loc[cycle, 'H{}'.format(head + 1)] = 'A'
else:
try:
slot_assign.loc[cycle, 'H{}'.format(head + 1)] = 'F{}'.format(
slot) if slot <= config.slot_num // 2 else 'R{}'.format(slot - config.head_num)
except:
print('')
print(slot_assign)
print('')
if place_hinter:
columns = ['H{}'.format(i + 1) for i in range(config.head_num)] + ['cycle']
place_assign = pd.DataFrame(columns=columns)
for cycle, _ in enumerate(opt_res.point):
place_assign.loc[cycle, 'cycle'] = 1
for head in range(config.head_num):
point = opt_res.point[cycle][head]
if point != -1:
place_assign.loc[cycle, 'H{}'.format(head + 1)] = 'P{}'.format(point)
else:
place_assign.loc[cycle, 'H{}'.format(head + 1)] = ''
headseq_assign = pd.DataFrame(columns=columns)
for cycle, headseq in enumerate(opt_res.sequence):
headseq_assign.loc[cycle, 'cycle'] = 1
for head in range(len(headseq)):
headseq_assign.loc[cycle, 'H{}'.format(head + 1)] = 'H{}'.format(headseq[head])
print(place_assign)
print(headseq_assign)
print('')
def evaluation(config: MachineConfig, part_data, step_data, opt_res: OptResult, hinter=False):
# === <20>Ż<EFBFBD><C5BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ===
info = OptInfo()
# === У<><D0A3> ===
info.total_points = 0
for cycle, part in enumerate(opt_res.part):
for head, component in enumerate(part):
if component == -1:
continue
info.total_points += opt_res.cycle[cycle]
if info.total_points != len(step_data):
warning_info = 'the number of ' + str(info.total_points) + ' placement point(s) is not match with the PCB data. '
warnings.warn(warning_info, UserWarning)
interval_ratio = config.head_intv / config.slot_intv
if opt_res.point:
total_points = info.total_points
for placements in opt_res.point:
for placement in placements:
if placement == -1:
continue
total_points -= 1
if total_points != 0:
warnings.warn(
'the optimization result of component assignment result and placement result are not consistent. ',
UserWarning)
return OptInfo()
feeder_arrangement = defaultdict(set)
for cycle, feeder_slots in enumerate(opt_res.slot):
for head, slot in enumerate(feeder_slots):
if slot == -1:
continue
feeder_arrangement[opt_res.part[cycle][head]].add(slot)
info.total_components = len(feeder_arrangement.keys())
for part, data in part_data.iterrows():
if part in feeder_arrangement.keys() and data.fdn < len(feeder_arrangement[part]):
info = 'the number of arranged feeder of [' + data['part'] + '] exceeds the quantity limit'
warnings.warn(info, UserWarning)
cur_pos, next_pos = config.anc_pos, Point(0, 0) # <20><>װͷ<D7B0><CDB7>ǰλ<C7B0><CEBB>
# <20><>ʼ<EFBFBD><CABC><EFBFBD>׸<EFBFBD><D7B8><EFBFBD><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD><EFBFBD><EFBFBD>װ<EFBFBD><D7B0><EFBFBD><EFBFBD>Ϣ
nozzle_assigned = ['Empty' for _ in range(config.head_num)]
for head in range(config.head_num):
for cycle in range(len(opt_res.part)):
idx = opt_res.part[cycle][head]
if idx == -1:
continue
else:
nozzle_assigned[head] = part_data.loc[idx]['nz']
for cycle_set, _ in enumerate(opt_res.part):
floor_cycle, ceil_cycle = sum(opt_res.cycle[:cycle_set]), sum(opt_res.cycle[:(cycle_set + 1)])
for cycle in range(floor_cycle, ceil_cycle):
if sum(opt_res.part[cycle_set]) == -config.head_num:
continue
pick_slot, mount_pos, mount_angle = [], [], []
nozzle_pick_counter, nozzle_put_counter = 0, 0 # <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͳ<EFBFBD>ƣ<EFBFBD>ʰȡ/<2F><><EFBFBD>÷ֱ<C3B7><D6B1><EFBFBD>һ<EFBFBD>Σ<EFBFBD>
for head in range(config.head_num):
if opt_res.slot[cycle_set][head] != -1:
pick_slot.append(opt_res.slot[cycle_set][head] - interval_ratio * head)
if opt_res.part[cycle_set][head] == -1:
continue
nozzle = part_data.loc[opt_res.part[cycle_set][head]]['nz']
if nozzle != nozzle_assigned[head]:
if nozzle_assigned[head] != 'Empty':
nozzle_put_counter += 1
nozzle_pick_counter += 1
nozzle_assigned[head] = nozzle
# ANC<4E><43><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if nozzle_pick_counter + nozzle_put_counter > 0:
next_pos = config.anc_pos
move_time = max(axis_moving_time(cur_pos.x - next_pos.x, 0),
axis_moving_time(cur_pos.y - next_pos.y, 1))
info.round_time += move_time
info.anc_round_counter += 1
info.total_distance += max(abs(cur_pos.x - next_pos.x), abs(cur_pos.y - next_pos.y))
cur_pos = next_pos
pick_slot = list(set(pick_slot))
pick_slot = sorted(pick_slot, reverse=True)
# ʰȡ·<C8A1><C2B7>(<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)
for idx, slot in enumerate(pick_slot):
if slot < config.slot_num // 2:
next_pos = Point(config.slotf1_pos.x + config.slot_intv * (slot - 1), config.slotf1_pos.y)
else:
next_pos = Point(config.slotr1_pos.x - config.slot_intv * (config.slot_num - slot - 1), config.slotr1_pos.y)
info.operation_time += config.pick_time
info.pickup_counter += 1
move_time = max(axis_moving_time(cur_pos.x - next_pos.x, 0),
axis_moving_time(cur_pos.y - next_pos.y, 1))
if idx == 0:
info.round_time += move_time
else:
info.pickup_time += move_time
info.total_distance += max(abs(cur_pos.x - next_pos.x), abs(cur_pos.y - next_pos.y))
if slot != pick_slot[0]:
info.pickup_distance += max(abs(cur_pos.x - next_pos.x), abs(cur_pos.y - next_pos.y))
cur_pos = next_pos
# <20><>װ·<D7B0><C2B7>
if opt_res.point and opt_res.sequence:
head_angle = [0 for _ in range(config.head_num)]
for head in opt_res.sequence[cycle]:
index = opt_res.point[cycle][head]
if index == -1:
continue
mount_pos.append(Point(step_data.loc[index].x - head * config.head_intv + config.stopper_pos.x,
step_data.loc[index].y + config.stopper_pos.y))
head_angle[head] = step_data.loc[index]['r']
# <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>װ·<D7B0><C2B7>
for cntPoints in range(len(mount_pos) - 1):
info.place_distance += max(abs(mount_pos[cntPoints].x - mount_pos[cntPoints + 1].x),
abs(mount_pos[cntPoints].y - mount_pos[cntPoints + 1].y))
if mount_pos[0].x < mount_pos[-1].x:
mount_pos = reversed(mount_pos)
# <20><><EFBFBD><EFBFBD>R<EFBFBD><52>Ԥ<EFBFBD><D4A4>ת<EFBFBD><D7AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬ<EFBFBD><CDAC><EFBFBD>Ƕ<EFBFBD>ת<EFBFBD><D7AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ķ<EFBFBD><C4B6><EFBFBD><EFBFBD><EFBFBD>װ<EFBFBD><D7B0>ʱ
info.operation_time += config.nozzle_install_time * nozzle_put_counter + \
config.nozzle_uninstall_time * nozzle_pick_counter
for idx, pos in enumerate(mount_pos):
info.operation_time += config.place_time
if idx == 0:
move_time = max(axis_moving_time(cur_pos.x - pos.x, 0),
axis_moving_time(cur_pos.y - pos.y, 1))
info.round_time += move_time
else:
cur_head = opt_res.sequence[cycle][idx]
side_head = cur_head - 1 if cur_head % 2 else cur_head + 1
if opt_res.sequence[cycle][idx - 1] != side_head:
move_time = max(axis_moving_time(cur_pos.x - pos.x, 0),
axis_moving_time(cur_pos.y - pos.y, 1))
else:
move_time = max(axis_moving_time(cur_pos.x - pos.x, 0),
axis_moving_time(cur_pos.y - pos.y, 1),
axis_moving_time(head_angle[cur_head] - head_angle[side_head], 2))
info.place_time += move_time
info.total_distance += max(abs(cur_pos.x - pos.x), abs(cur_pos.y - pos.y))
cur_pos = pos
info.nozzle_change_counter += nozzle_put_counter + nozzle_pick_counter
info.total_time = info.pickup_time + info.round_time + info.place_time + info.operation_time
info.cycle_counter = sum(opt_res.cycle)
if hinter:
optimizer_result_hinter(config, part_data, opt_res, part_hinter=True, nozzle_hinter=True, slot_hinter=True)
info.print()
return info