优化器类的定义和实现
This commit is contained in:
279
opt/utils.py
Normal file
279
opt/utils.py
Normal 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user