LoginSignup
1
1

More than 1 year has passed since last update.

Blenderで待ち行列

Last updated at Posted at 2022-03-03

Blenderで待ち行列を可視化すると面白いんじゃないかと思ってやってみたことの紹介です。

うまく動かすのが難しかったので、適当になってます。

待ち行列は、M/M/1でやってみたら、指数分布だと間延びするので、到着間隔とサービス時間は対数正規分布にしました。

ライブラリーはSimPyを使います。「Blenderのコマンドサンプル」を参考にBlenderのPythonにインストールしてください。

待ち行列とSimPyについては、「待ち行列について」も参考にしてください。

モデル作成

顧客と待ち行列とサーバーのモデルを作ります。

  • 顧客は、サクッとスザンヌにしましょう。名前はcustにします。複製するので、適当なコレクションに入れておきます。
  • 待ち行列は、並ぶ場所だけ作ります。ここではカーブを適当に作ります。名前はpathにします。
  • サーバーは、動かさないので何でもいいので作ります。名前も適当で構いません。

コンストレイントの設定

custpathに沿うようにします。
custpathの順に選択し、Ctrl + P(ペアレント)のパス追従コンストレイントを選びます。

オブジェクトコンストレイントプロパティを以下のように設定します。

適当に色をつけると以下のようになります。

シミュレーションの実行とアニメーションの設定

Scriptingワークスペースで新規を押し、下記をコピペして実行します。

import bpy
import random
import simpy
from collections import deque

class Customer:
    def __init__(self, tim, pos):
        self.events = [(0, -1), (tim-1e-3, -1), (tim, pos)]  # 時刻と位置のリスト
    def __repr__(self):
        return str([(round(t, 2), p) for t, p in self.events])

class Sim:
    def __init__(self, la, mu, timwalk=1):
        self.la = la  # 平均到着率
        self.mu = mu  # 平均サービス率
        self.timwalk = timwalk  # 1つ詰める時間
        self.env = simpy.Environment()
        self.queue = deque()  # 待ち行列
        self.customers = []  # 到着した顧客

    def arrive(self):
        """到着イベント"""
        while True:
            yield self.env.timeout(random.lognormvariate(0, self.la))
            self.env.process(self.into_queue())

    def into_queue(self):
        """待ち行列に並ぶ"""
        global queue, customers
        self.customers.append(Customer(self.env.now, len(self.queue)))
        self.queue.append(self.customers[-1])
        if len(self.queue) > 1:
            return
        while len(self.queue) > 0:
            yield self.env.timeout(random.lognormvariate(0, self.mu))
            cust = self.queue.popleft()
            cust.events.append((self.env.now-1e-3, 0))
            cust.events.append((self.env.now, -1))
            for cust in self.queue:
                cust.events.append((self.env.now, cust.events[-1][1]))
            yield self.env.timeout(self.timwalk)
            for i, cust in enumerate(self.queue):
                cust.events.append((self.env.now, i))

    def run(self, simtim, seed=None):
        self.env.process(self.arrive())
        random.seed(seed)
        self.env.run(simtim)
        return self.customers

# シミュレーション実行
customers = Sim(0.2, 0.1).run(simtim=10, seed=0)

# アニメーションの設定
org = bpy.data.objects["cust"]
stp = 10
for customers in customers:
    bpy.ops.object.select_all(action="DESELECT")
    org.select_set(state=True)
    bpy.ops.object.duplicate_move_linked(OBJECT_OT_duplicate={"linked": True})
    obj = bpy.context.selected_objects[0]
    cns = obj.constraints[0]
    for tm, pos in customers.events:
        cns.offset = 0 if pos < 0 else -100 + pos * stp
        cns.keyframe_insert(data_path='offset', frame=tm * 30)

参考:BlenderでPythonを実行する方法

アニメーションでは、コンストレイントのオフセットを変えています。
オフセットが0だと開始位置、-100で終了位置になっています。

レンダリング

カメラとライトを設定してレンダリングしてみましょう。

h7obt-2g51q.gif

ちょっと動きが変ですが、直せなかったのでこのままで。

以上

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1