PCB Manufacturing Scheduling
このサンプルでは、Printed Circuit Board(PCB) の生産スケジュール問題を取り扱います。
問題設定
ここでは PCB には挿入実装と表面実装の部品が存在し、それぞれ別工程で実装することを考えます。生産工程は下記のように考えます。
表面実装工程: 表面実装のはんだ付け(マウンタ)
挿入実装工程: 挿入実装のはんだ付け(卓上型はんだ付けロボット)
検査工程: 部品の検査
一つの PCB の生産は表面実装工程、挿入実装工程、検査工程の 3 つの工程で完成されます。表面実装工程では一つのマウンタで実装され、挿入実装工程は PCB ごとに決められた数の卓上はんだ付けロボットで実装され、最後に検査工程で PCB ごとに決められた検査手順を行います。
また、挿入実装工程と検査工程はそれぞれの工程を開始したら中断することができないとします。
表面実装工程
表面実装工程では、表面実装のはんだ付けを行うマウンタが 3 つ(マウンタ A, マウンタ B、マウンタ C)あるとします。 それぞれのマウンタは、PCB を一つずつ実装することができます。
挿入実装工程
挿入実装工程では、卓上型はんだ付けロボットが 6 つ(卓ロボ A, 卓ロボ B, 卓ロボ C, 卓ロボ D, 卓ロボ E, 卓ロボ F)があるとします。各 PCB は決められた複数の卓ロボで決められた順序で実装されますが、一度実装を始めた場合は、挿入実装工程を完了する必要があります。
検査工程
検査工程では、PCB ごとに決められた検査手順を行います。検査工程は、PCB ごとに決められた手順を行う検査手順 1,2,3,4 があり、検査工程は一度開始したら中断することができません。また検査工程ではリソースとして、作業員 1 人と PCB ごとに決められた検査治具が必要です。また同じ作業員が一貫して検査工程を行うほうが効率がよいとして、作業員の入れ替えにはコストがかかるとします。
from amplify_sched import *
import itertools
import pandas as pd
import random
num_operator = 3
num_pcb = 15
jig_list1 = ["治具{}".format(i) for i in range(1, 4)]
jig_list2 = ["治具{}".format(i) for i in range(4, 7)]
jig_list3 = ["治具{}".format(i) for i in range(7, 10)]
jig_list4 = ["治具{}".format(i) for i in range(10, 15)]
jig_capacity = 300
mounters = ["マウンタA", "マウンタB", "マウンタC"]
takurobos = ["卓ロボA", "卓ロボB", "卓ロボC", "卓ロボD", "卓ロボE", "卓ロボF"]
operators = ["従業員A", "従業員B", "従業員C"]
checks = ["検査手順1", "検査手順2", "検査手順3", "検査手順4"]
operator_change_time = 100
d = {
"PCB": [],
"マウンタA": [],
"マウンタB": [],
"マウンタC": [],
"卓ロボA": [],
"卓ロボB": [],
"卓ロボC": [],
"卓ロボD": [],
"卓ロボE": [],
"卓ロボF": [],
"検査手順1": [],
"検査手順2": [],
"検査手順3": [],
"検査手順4": [],
"検査治具1": [],
"検査治具2": [],
"検査治具3": [],
"検査治具4": [],
}
cols = pd.MultiIndex.from_tuples(
[
("表面実装工程", "マウンタA"),
("表面実装工程", "マウンタB"),
("表面実装工程", "マウンタC"),
("挿入実装工程", "卓ロボA"),
("挿入実装工程", "卓ロボB"),
("挿入実装工程", "卓ロボC"),
("挿入実装工程", "卓ロボD"),
("挿入実装工程", "卓ロボE"),
("挿入実装工程", "卓ロボF"),
("検査手順1", "従業員A"),
("検査手順1", "従業員B"),
("検査手順1", "従業員C"),
("検査手順2", "従業員A"),
("検査手順2", "従業員B"),
("検査手順2", "従業員C"),
("検査手順3", "従業員A"),
("検査手順3", "従業員B"),
("検査手順3", "従業員C"),
("検査手順4", "従業員A"),
("検査手順4", "従業員B"),
("検査手順4", "従業員C"),
("治具", "検査手順1"),
("治具", "検査手順2"),
("治具", "検査手順3"),
("治具", "検査手順4"),
]
)
index = pd.Index(["PCB{}".format(i + 1) for i in range(num_pcb)], name="PCB")
data = []
for i in range(num_pcb):
d = []
d.append(random.randint(10, 30)) # マウンタA
d.append(random.randint(10, 30)) # マウンタB
d.append(random.randint(10, 30)) # マウンタC
d.append(random.randint(0, 30)) # 卓ロボA
d.append(random.randint(0, 30)) # 卓ロボB
d.append(random.randint(0, 30)) # 卓ロボC
d.append(random.randint(0, 30)) # 卓ロボD
d.append(random.randint(0, 30)) # 卓ロボE
d.append(random.randint(0, 30)) # 卓ロボF
d.append(random.randint(10, 30)) # 検査手順1: 従業員A
d.append(random.randint(10, 30)) # 検査手順1: 従業員B
d.append(random.randint(10, 30)) # 検査手順1: 従業員C
d.append(random.randint(10, 30)) # 検査手順2: 従業員A
d.append(random.randint(10, 30)) # 検査手順2: 従業員B
d.append(random.randint(10, 30)) # 検査手順2: 従業員C
d.append(random.randint(10, 30)) # 検査手順3: 従業員A
d.append(random.randint(10, 30)) # 検査手順3: 従業員B
d.append(random.randint(10, 30)) # 検査手順3: 従業員C
d.append(random.randint(10, 30)) # 検査手順4: 従業員A
d.append(random.randint(10, 30)) # 検査手順4: 従業員B
d.append(random.randint(10, 30)) # 検査手順4: 従業員C
d.append(random.choices(jig_list1, k=2)) # 治具: 検査手順1
d.append(random.choices(jig_list2, k=2)) # 治具: 検査手順2
d.append(random.choices(jig_list3, k=2)) # 治具: 検査手順3
d.append(random.choices(jig_list4, k=3)) # 治具: 検査手順4
data.append(d)
df = pd.DataFrame(data, columns=cols, index=index)
df["表面実装工程"]
マウンタA | マウンタB | マウンタC | |
---|---|---|---|
PCB | |||
PCB1 | 24 | 25 | 30 |
PCB2 | 22 | 29 | 12 |
PCB3 | 10 | 19 | 19 |
PCB4 | 23 | 24 | 15 |
PCB5 | 24 | 30 | 27 |
PCB6 | 28 | 18 | 18 |
PCB7 | 30 | 24 | 26 |
PCB8 | 15 | 28 | 11 |
PCB9 | 15 | 22 | 16 |
PCB10 | 20 | 10 | 25 |
PCB11 | 24 | 22 | 27 |
PCB12 | 27 | 27 | 20 |
PCB13 | 20 | 25 | 23 |
PCB14 | 24 | 13 | 24 |
PCB15 | 25 | 17 | 20 |
df["挿入実装工程"]
卓ロボA | 卓ロボB | 卓ロボC | 卓ロボD | 卓ロボE | 卓ロボF | |
---|---|---|---|---|---|---|
PCB | ||||||
PCB1 | 0 | 4 | 5 | 27 | 13 | 18 |
PCB2 | 7 | 9 | 26 | 4 | 14 | 20 |
PCB3 | 8 | 7 | 7 | 20 | 28 | 20 |
PCB4 | 11 | 4 | 12 | 10 | 4 | 28 |
PCB5 | 7 | 29 | 20 | 9 | 13 | 6 |
PCB6 | 14 | 30 | 2 | 30 | 3 | 13 |
PCB7 | 18 | 25 | 19 | 3 | 9 | 30 |
PCB8 | 26 | 20 | 10 | 27 | 24 | 14 |
PCB9 | 29 | 13 | 25 | 14 | 18 | 20 |
PCB10 | 23 | 25 | 15 | 23 | 7 | 5 |
PCB11 | 3 | 22 | 6 | 29 | 30 | 0 |
PCB12 | 5 | 28 | 13 | 24 | 23 | 9 |
PCB13 | 26 | 4 | 25 | 17 | 24 | 3 |
PCB14 | 17 | 15 | 4 | 11 | 27 | 20 |
PCB15 | 24 | 11 | 21 | 1 | 19 | 3 |
df["検査手順1"]
従業員A | 従業員B | 従業員C | |
---|---|---|---|
PCB | |||
PCB1 | 28 | 11 | 26 |
PCB2 | 19 | 27 | 17 |
PCB3 | 27 | 29 | 24 |
PCB4 | 29 | 17 | 10 |
PCB5 | 14 | 16 | 24 |
PCB6 | 11 | 16 | 27 |
PCB7 | 27 | 26 | 23 |
PCB8 | 10 | 14 | 30 |
PCB9 | 12 | 28 | 14 |
PCB10 | 20 | 17 | 16 |
PCB11 | 23 | 19 | 10 |
PCB12 | 24 | 18 | 28 |
PCB13 | 30 | 12 | 20 |
PCB14 | 30 | 18 | 27 |
PCB15 | 26 | 19 | 30 |
df["検査手順2"]
従業員A | 従業員B | 従業員C | |
---|---|---|---|
PCB | |||
PCB1 | 16 | 28 | 25 |
PCB2 | 20 | 20 | 28 |
PCB3 | 14 | 21 | 11 |
PCB4 | 24 | 21 | 20 |
PCB5 | 19 | 17 | 14 |
PCB6 | 30 | 30 | 25 |
PCB7 | 27 | 12 | 17 |
PCB8 | 14 | 27 | 25 |
PCB9 | 26 | 11 | 22 |
PCB10 | 13 | 16 | 24 |
PCB11 | 10 | 16 | 25 |
PCB12 | 28 | 11 | 25 |
PCB13 | 26 | 29 | 22 |
PCB14 | 10 | 29 | 29 |
PCB15 | 28 | 26 | 21 |
df["検査手順3"]
従業員A | 従業員B | 従業員C | |
---|---|---|---|
PCB | |||
PCB1 | 26 | 27 | 25 |
PCB2 | 29 | 23 | 24 |
PCB3 | 17 | 25 | 20 |
PCB4 | 28 | 16 | 21 |
PCB5 | 16 | 12 | 26 |
PCB6 | 22 | 18 | 21 |
PCB7 | 29 | 14 | 18 |
PCB8 | 15 | 24 | 24 |
PCB9 | 20 | 20 | 30 |
PCB10 | 22 | 27 | 12 |
PCB11 | 25 | 30 | 18 |
PCB12 | 30 | 26 | 19 |
PCB13 | 16 | 29 | 15 |
PCB14 | 13 | 19 | 29 |
PCB15 | 14 | 28 | 21 |
df["検査手順4"]
従業員A | 従業員B | 従業員C | |
---|---|---|---|
PCB | |||
PCB1 | 15 | 21 | 26 |
PCB2 | 12 | 16 | 23 |
PCB3 | 19 | 22 | 25 |
PCB4 | 21 | 26 | 19 |
PCB5 | 17 | 20 | 18 |
PCB6 | 29 | 24 | 17 |
PCB7 | 12 | 10 | 14 |
PCB8 | 15 | 28 | 20 |
PCB9 | 28 | 10 | 17 |
PCB10 | 24 | 23 | 10 |
PCB11 | 25 | 22 | 15 |
PCB12 | 13 | 14 | 21 |
PCB13 | 16 | 11 | 17 |
PCB14 | 24 | 20 | 24 |
PCB15 | 20 | 23 | 11 |
df["治具"]
検査手順1 | 検査手順2 | 検査手順3 | 検査手順4 | |
---|---|---|---|---|
PCB | ||||
PCB1 | [治具1, 治具1] | [治具5, 治具6] | [治具7, 治具8] | [治具14, 治具10, 治具13] |
PCB2 | [治具1, 治具2] | [治具5, 治具5] | [治具9, 治具8] | [治具13, 治具14, 治具10] |
PCB3 | [治具2, 治具2] | [治具6, 治具5] | [治具9, 治具8] | [治具14, 治具14, 治具10] |
PCB4 | [治具1, 治具1] | [治具4, 治具6] | [治具8, 治具8] | [治具14, 治具10, 治具12] |
PCB5 | [治具2, 治具3] | [治具4, 治具4] | [治具9, 治具8] | [治具11, 治具13, 治具14] |
PCB6 | [治具1, 治具1] | [治具5, 治具4] | [治具9, 治具8] | [治具10, 治具14, 治具12] |
PCB7 | [治具3, 治具1] | [治具4, 治具4] | [治具7, 治具8] | [治具12, 治具12, 治具14] |
PCB8 | [治具2, 治具1] | [治具4, 治具4] | [治具8, 治具8] | [治具11, 治具12, 治具12] |
PCB9 | [治具2, 治具2] | [治具4, 治具6] | [治具9, 治具7] | [治具11, 治具13, 治具14] |
PCB10 | [治具2, 治具1] | [治具6, 治具6] | [治具7, 治具8] | [治具13, 治具13, 治具12] |
PCB11 | [治具2, 治具3] | [治具5, 治具5] | [治具7, 治具9] | [治具12, 治具12, 治具12] |
PCB12 | [治具2, 治具1] | [治具6, 治具4] | [治具8, 治具9] | [治具14, 治具10, 治具14] |
PCB13 | [治具1, 治具1] | [治具5, 治具6] | [治具7, 治具7] | [治具14, 治具13, 治具10] |
PCB14 | [治具1, 治具1] | [治具5, 治具6] | [治具8, 治具7] | [治具11, 治具14, 治具11] |
PCB15 | [治具2, 治具3] | [治具4, 治具5] | [治具9, 治具8] | [治具11, 治具12, 治具12] |
Amplify Sched を用いた定式化
挿入実装工程と検査工程はそれぞれの工程を開始したら中断することができないので、Amplify Sched における no wait 制約を用いて定式化します。
表面実装工程、挿入実装工程、検査工程をそれぞれ別の Job として、Job 間の順序制約を課します。
model = Model()
for j in df.index:
model.jobs.add(j + "表面実装工程")
model.jobs.add(j + "挿入実装工程")
model.jobs[j + "挿入実装工程"].no_wait = True
model.jobs[j + "挿入実装工程"].add_dependent_jobs(model.jobs[j + "表面実装工程"])
model.jobs.add(j + "検査工程")
model.jobs[j + "検査工程"].no_wait = True
model.jobs[j + "検査工程"].add_dependent_jobs(model.jobs[j + "挿入実装工程"])
for m in mounters + takurobos + operators:
model.machines.add(m)
for k in jig_list1 + jig_list2 + jig_list3 + jig_list4:
model.resources.add(k)
model.resources[k].capacity = jig_capacity
for j in df.index:
# 表面実装工程
model.jobs[j + "表面実装工程"].append(Task())
for m in mounters:
model.jobs[j + "表面実装工程"][0].processing_times[m] = int(df.loc[j, "表面実装工程"][m])
# 挿入実装工程
for i, m in enumerate(takurobos):
model.jobs[j + "挿入実装工程"].append(Task())
model.jobs[j + "挿入実装工程"][i].processing_times[m] = int(df.loc[j, "挿入実装工程"][m])
# 検査工程
for i, p in enumerate(checks[0:2]):
model.jobs[j + "検査工程"].append(Task())
for m in operators:
model.jobs[j + "検査工程"][i].processing_times[m] = int(df.loc[j, p][m])
for r in df.loc[j, "治具"][p]:
model.jobs[j + "検査工程"][i].add_required_resource(r)
for m1, m2 in itertools.combinations(operators, 2):
model.jobs[j + "検査工程"][i].add_transportation_time(operator_change_time, m1, m2)
model.jobs[j + "検査工程"][i].add_transportation_time(operator_change_time, m2, m1)
token = "xxxxxxxxxxxxxxxxxxxxxx"
gantt = model.solve(token=token, timeout=10)
gantt.timeline(machine_view=True)