psim言語講座(第16回)チュートリアル編(7)(続)psim のストアを使いこなす

  • HOME
  • psim言語講座(第16回)チュートリアル編(7)(続)psim のストアを使いこなす

本記事は当社が発行しているシミュレーションメールマガジンVol. 16の記事です。
シミュレーションメールマガジンの詳細・購読申込はこちら のサポートページから

今回は、前回に続き、離散イベントシミュレーションでよく発生するような、より具体的なストアの応用例について説明しようと思います。

工場の生産ラインのAGVによる搬送

前回は、工場の生産ライン上にある。そのひとつの加工機械のみに着目しました。

加工機械の前には製品の置き場(s)があり、加工機械(f)は、製品の置き場から製品をひとつ取得し、加工し、加工済みの製品を、加工機械の後の製品置き場(t)におくとしました。

工場の生産ラインには、このような加工機械が複数あります。

加工機械間の搬送も自動化される事が多く、例えば、加工機械間は人が搬送する時もありますし、加工機械間がベルトコンベアで接続されている事もあり、AGV(無人搬送車)が搬送する事もあります。

その搬送ルールについても様々な方法がありますが、今回は、AGV による単純なループ搬送を考えます。

今回作成する生産ラインを、もう少し詳細に設計します。

まず、工程については、以下のように定義します。

  • 工程は 4 つあります。
  • 工程0 には、初期に、生産する製品が全て置かれています。
  • 工程1 には、加工前の製品置き場と加工後の製品置き場があります。
  • 工程2 には、加工前の製品置き場と加工後の製品置き場があります。
  • 工程3 には、生産済みの製品置き場です。
  • 各製品には、それぞれ加工順序が定義されています。
  • 工程1、2の、加工前の製品置き場に製品が置かれると、自動的に加工を行い、加工終了後に、加工後の製品置き場に置かれます。
  • 工程1、2の、加工前と加工後の製品置き場には1つのみ製品を置く事ができます。
  • 工程1、2の、加工機械は同時に1つのみ製品を加工できます。

次に、工程については、以下のように定義します。

  • AGV は 3台あります。
  • それぞれの AGV は、工程0, 1, 2, 3 を順番に巡回します。工程3の後は 工程0に移動します。
  • AGV には高々 1 の製品を搬送できます。
  • AGV が 工程0 に差し掛かった時、製品を保持しておらず、在庫があれば、取得します。
  • AGV が 工程1 に差し掛かった時、製品を保持しておらず、加工後の製品置き場に製品があれば、取得します。あるいは、製品を保持しており、次の加工順番が工程1の場合は、工程1の加工前の製品置き場に製品を置きます。
  • AGV が 工程2 に差し掛かった時、製品を保持しておらず、加工後の製品置き場に製品があれば、取得します。あるいは、製品を保持しており、次の加工順番が工程2の場合は、工程2の加工前の製品置き場に製品を置きます。
  • AGV が 工程3 に差し掛かった時、製品を保持しており、次の加工順番が工程3の場合は、工程3に製品を置きます。

では、実際にコードを書いていきます。

まず、工程の定義は、以下のように書けます。生産ラインの定義をそのまま落とし込む事ができます。

    t0 = Store(False, monitor = True)

    s1 = Store(1, monitor = True)
    f1 = Facility(1, monitor = True)
    t1 = Store(1, monitor = True)

    s2 = Store(1, monitor = True)
    f2 = Facility(1, monitor = True)
    t2 = Store(1, monitor = True)

    s3 = Store(False, monitor = True)

各製品には、それぞれ加工順序が定義されていますが、それを、分かり易くするために、Product クラスを定義します。

    class Product:
        def __init__(self, name, instructions):
            self.name = name
            self.instructions = instructions

製品の初期配置を作成するためのコードは以下になります。製品毎に、加工順番を定義しています。この例では、6 製品に、それぞれ加工順番を定義しています。

    def init():
        for name, instructions in [
                ("製品1", [1, 2, 3]),
                ("製品2", [1, 3]),
                ("製品3", [2, 3]),
                ("製品4", [2, 1, 3]),
                ("製品5", [1, 3]),
                ("製品6", [2, 3])]:
            prod = Product(name, instructions)
            yield t0.put1(prod, name = "put1")

工程1の実装は、前回のひとつの工程定義そのままです。

    def process1():
        yield pause(10)
        while True:
            result = yield s1.get1(name = "get")
            item = result["get"]
            print(f"時間 {now()} 工程1 作業開始 {item.name}")
            lock = (yield f1.request(name = "lock"))["lock"]
            yield pause(10)
            lock.release()
            print(f"時間 {now()} 工程1 作業終了 {item.name}")
            yield t1.put1(item, name = "put")

工程2の実装も、前回のひとつの工程定義そのままです。

    def process2():
        yield pause(10)
        while True:
            result = yield s2.get1(name = "get")
            item = result["get"]
            print(f"時間 {now()} 工程2 作業開始 {item.name}")
            lock = (yield f2.request(name = "lock"))["lock"]
            yield pause(10)
            lock.release()
            print(f"時間 {now()} 工程2 作業終了 {item.name}")
            yield t2.put1(item, name = "put")

次に、AGV を 3 台、並列に動かします。

    def init_agv():
        for i in range(3):
            yield subactivate(agv)(f"AGV{i}")
        yield alwaysFalse()

各AGVの動作を以下のように定義しています。各AGVは高々 1 の製品を搬送できますが、それが prod です。何も搬送していない時は None、搬送している時は製品を保持します。

それぞれの AGV は、工程0, 1, 2, 3 を順番に巡回し、工程3の後は 工程0に移動するので、ループとして実装します。

AGV が 工程0 に差し掛かった時、製品を保持しておらず、在庫があれば、取得するという仕様を、その通りに実装しています。

AGV が 工程1 に差し掛かった時、製品を保持しておらず、加工後の製品置き場に製品があれば、取得し、あるいは、製品を保持しており、次の加工順番が工程1の場合は、工程1の加工前の製品置き場に製品を置くという仕様を、その通りに実装しています。

AGV が 工程3 に差し掛かった時、製品を保持しており、次の加工順番が工程3の場合は、工程3に製品を置くという仕様も、その通りに実装しています。

    def agv(agvname):
        prod = None
        while True:
            if prod is None:
                if t0.sizeBuffer() > 0:
                    prod = (yield t0.get1(name = "get"))["get"]
                    print(f"時間 {now()} {agvname} 取得 工程0")
            yield pause(10)
            if prod is None:
                if t1.sizeBuffer() > 0:
                    prod = (yield t1.get1(name = "get"))["get"]
                    print(f"時間 {now()} {agvname} 取得 工程1")
            elif prod.instructions[0] == 1:
                prod.instructions.pop(0)
                print(f"時間 {now()} {agvname} 配置待ち 工程1")
                yield s1.put1(prod, name = "put")
                print(f"時間 {now()} {agvname} 配置完了 工程1")
                prod = None
            yield pause(10)
            if prod is None:
                if t2.sizeBuffer() > 0:
                    prod = (yield t2.get1(name = "get"))["get"]
                    print(f"時間 {now()} {agvname} 取得 工程2")
            elif prod.instructions[0] == 2:
                prod.instructions.pop(0)
                print(f"時間 {now()} {agvname} 配置待ち 工程2")
                yield s2.put1(prod, name = "put")
                print(f"時間 {now()} {agvname} 配置完了 工程2")
                prod = None
            yield pause(10)
            if prod is not None and prod.instructions[0] == 3:
                yield s3.put1(prod, name = "put")
                print(f"時間 {now()} {agvname} 配置 工程3")
                prod = None
            yield pause(10)

以下が、起動コードです。

    activate(init)()
    activate(process1)()
    activate(process2)()
    activate(init_agv)()
    start(until = 300)

実行結果は以下のようになります。

時間 0.0 AGV0 取得 工程0
時間 0.0 AGV1 取得 工程0
時間 0.0 AGV2 取得 工程0
時間 10.0 AGV0 配置待ち 工程1
時間 10.0 AGV1 配置待ち 工程1
時間 10.0 AGV0 配置完了 工程1
時間 10.0 工程1 作業開始 製品1
時間 10.0 AGV1 配置完了 工程1
時間 20.0 AGV2 配置待ち 工程2
時間 20.0 工程1 作業終了 製品1
時間 20.0 AGV2 配置完了 工程2
時間 20.0 工程2 作業開始 製品3
時間 20.0 工程1 作業開始 製品2
時間 30.0 工程2 作業終了 製品3
時間 30.0 工程1 作業終了 製品2
時間 40.0 AGV0 取得 工程0
時間 40.0 AGV1 取得 工程0
時間 40.0 AGV2 取得 工程0
時間 50.0 AGV1 配置待ち 工程1
時間 50.0 AGV1 配置完了 工程1
時間 60.0 AGV0 配置待ち 工程2
時間 60.0 AGV2 配置待ち 工程2
時間 60.0 AGV0 配置完了 工程2
時間 60.0 工程2 作業開始 製品4
時間 60.0 AGV2 配置完了 工程2
時間 60.0 AGV1 取得 工程2
時間 70.0 工程2 作業終了 製品4
時間 70.0 AGV1 配置 工程3
時間 70.0 工程2 作業開始 製品6
時間 80.0 工程2 作業終了 製品6
時間 90.0 AGV0 取得 工程1
時間 90.0 AGV2 取得 工程1
時間 90.0 工程1 作業開始 製品5
時間 100.0 AGV0 配置待ち 工程2
時間 100.0 工程1 作業終了 製品5
時間 100.0 AGV0 配置完了 工程2
時間 100.0 AGV1 取得 工程1
時間 110.0 AGV2 配置 工程3
時間 120.0 AGV1 配置 工程3
時間 140.0 AGV0 取得 工程2
時間 140.0 AGV2 取得 工程2
時間 140.0 工程2 作業開始 製品1
時間 150.0 工程2 作業終了 製品1
時間 150.0 AGV2 配置 工程3
時間 170.0 AGV0 配置待ち 工程1
時間 170.0 AGV0 配置完了 工程1
時間 170.0 工程1 作業開始 製品4
時間 180.0 工程1 作業終了 製品4
時間 180.0 AGV2 取得 工程2
時間 190.0 AGV2 配置 工程3
時間 210.0 AGV2 取得 工程1
時間 230.0 AGV2 配置 工程3

まとめ

今回は、Store の応用例の続きとして、AGV による搬送があるような生産ラインの例を説明しました。

個々の生産ラインは前回のひとつの加工機械の実装をそのまま利用し、それらを、順番に辿るような AGV を設計、実装しました。

今回は非常に単純なAGV の動作を実装しています。実際の生産ラインの AGV は、より複雑な動作をする場合もあります。次回は、違った振舞いをするような AGV についても考えていきたいと思います。

監修:株式会社NTTデータ数理システム 機械学習、統計解析、数理計画、シミュレーションなどの数理科学を 背景とした技術を活用し、業種・テーマを問わず幅広く仕事をしています。
http://www.msi.co.jp NTTデータ数理システムができること
「数理科学の基礎知識」e-book無料ダウンロードはこちら

関連記事