How to model a conveyor

400 views
Skip to first unread message

David García

unread,
Oct 27, 2023, 4:25:45 PM10/27/23
to python-simpy
Hello, i am new in simpy, and I would like to model a warehouse. I think i am able to model resouces and movement of pallets in the model, but i am trying to wonder how to model the moveent of parcels with conveyors.

What would it be the best approach to model a belt or group of connected belts?

Thanks for your help!

David

cesar amable

unread,
Oct 28, 2023, 5:46:49 AM10/28/23
to David García, python-simpy
Hello David,

Interesting project.  I assume you are referring to the accumulation type of conveyor systems (refer to links below).  I would think that you can start with a store type resource where the parcels are the objects.  As the parcels are different sizes and the length of a given conveyor is finite you could use some random distribution for the footprint of the parcels.  Try also Bing chat that gives some interesting code (link below) with a different approach.

Hopefully another friend from the group will provide more insight.

Regards,

Cesar Amable










------ Original Message ------
From "David García" <davi...@gmail.com>
To "python-simpy" <python...@googlegroups.com>
Date 10/27/2023 4:25:44 PM
Subject [SimPy] How to model a conveyor

--
You received this message because you are subscribed to the Google Groups "python-simpy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python-simpy...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python-simpy/49469e69-18f0-4edc-ba92-75574db15e48n%40googlegroups.com.

Sean Reed

unread,
Oct 28, 2023, 9:32:33 AM10/28/23
to python-simpy
Hi David,

I have some expertise here from building many models of discrete manufacturing systems. The implementation depends somewhat on the type of conveyor you are modelling as Cesar points out (e.g. are the parcels homogeneous or do you need to represent them as individual objects with their own data? does the belt run at constant speed or can it change? Is it accumulating (do parcels bunch up at the end of the conveyor or does the conveyor stop when a parcel reaches the end)? etc.) I can give some pointers however on modelling a simple conveyor that runs at constant speed and you can then figure out the details for your specific case.

You will need the following:
  • Variables l and s representing the length and speed of the conveyor.
  • Variable last_added representing the simulation time that an item was last added to the conveyor.
  • A list items to store the parcel instances (assuming they are non-homogeneous).
  • A list distances to store the distance of each parcel to the previously added parcel along the conveyor belt. 
Then when an item is added to the conveyor:
  • Append the item to list items.
  • If the item was added to an empty belt (len(items) == 1), append the conveyor length to list distances. Otherwise, calculate the distance the belt traveled since the last item was added (i.e. (env.now - last_added)*s) and append that instead.
  •  Set last_added to the current simulation time (env.now).
Then all you need is an event loop to model the arrival of items at the end of the conveyor (e.g. while items). The timeout for the next arrival event is the distance at the first entry in distances divided by the conveyor speed s. When this event occurs you remove the first items from items and distances and then schedule the next arrival event in the same way.

To model a group of connected conveyors, you could store a reference to the successor conveyor in each conveyor instance, then when an item arrives at an end of the conveyor, insert it into the front of the successor conveyor.

Best regards,

Sean

David García

unread,
Oct 28, 2023, 10:13:35 AM10/28/23
to se...@sean-reed.com, python-simpy
Thank you very much for your help.

i would like to make it as simple as possible, because my objetive is more or less high level simulations to use it for evaluate several layouts and to be a fitness function for an optimization model.

In this situation I would consider all of then as no acummulative belts (lets say that I would see them as sorters of boxes or totes between different working stations)

i tried to simulate like simpy stores with no success...




Sean Reed

unread,
Oct 29, 2023, 10:50:28 AM10/29/23
to python-simpy
Example code:

import simpy
import random


class Conveyor:
    def __init__(self, env, length, speed):
        self.env = env
        self.length = length
        self.speed = speed
        self.items = []
        self.distances = []
        self.last_added_time = env.now

    def add_item(self, item):
        self.items.append(item)
        if len(self.items) == 1:
            self.distances.append(self.length)
            self.env.process(self.convey_items())  # Start conveying items to end of conveyor.
        else:
            distance_to_previous = self.speed * (self.env.now - self.last_added_time)
            self.distances.append(distance_to_previous)

        self.last_added_time = self.env.now

    def convey_items(self):
        while self.items:
            item = self.items.pop(0)
            distance_to_previous = self.distances.pop(0)
            yield self.env.timeout(distance_to_previous / self.speed)
            print(f"Item {item} reached end of conveyor at {self.env.now:.2f}.")


def add_items(env, conveyor):
    """Add new items to the conveyor every few simulated time units."""
    for i in range(5):
        yield env.timeout(random.uniform(0, 10))
        conveyor.add_item(i)
        print(f"Item {i} added to conveyor at {env.now:.2f}.")


if __name__ == "__main__":
    random.seed(42)
    env = simpy.Environment()
    conveyor = Conveyor(env, length=10, speed=2)
    env.process(add_items(env, conveyor))
    env.run()

    '''
    Output:
   
    Item 0 added to conveyor at 6.39.
    Item 1 added to conveyor at 6.64.
    Item 2 added to conveyor at 9.39.
    Item 0 reached end of conveyor at 11.39.
    Item 3 added to conveyor at 11.63.
    Item 1 reached end of conveyor at 11.64.
    Item 2 reached end of conveyor at 14.39.
    Item 3 reached end of conveyor at 16.63.
    Item 4 added to conveyor at 18.99.
    Item 4 reached end of conveyor at 23.99.
    '''

Michael Gibbs

unread,
Nov 3, 2023, 10:18:32 PM11/3/23
to python-simpy
here is another example.  In this example when a package gets to the end of the conveyor, the conveyor stops until the package is removed.  The trick illustrated here is how a group of packages start and stop in sync with each other.

"""
A simple conveyor

has a load hopper to queue packanges
waiting to be conveyed.
When a package gets to the end of the
conveyor the conveyor stops until the
package is removed from the conveyor.

programmer: Michael R. Gibbs
"""

import simpy
import random

convey_time = 20 # time to move package to end of conveyor
convey_load_time = 2 # minimum time between packages placed on conveyor

class Package():
    """
    simple package class
    gives a id to each package
    """
   
    next_id = 1

    def __init__(self):

        self.pack_id = self.__class__.next_id
        self.__class__.next_id += 1


class Conveyor():
    """
    Simple conveyor that move stuff from point A to point B.
   
    Conveyor stops when a package gets to the end and
    restarts when the package is removed from the conveyor
    """

    def __init__(self, env):

        self.env = env

        self.load_hopper = simpy.Store(env)
        self.pick_up_pile = simpy.Store(env,1)

        # events to cordinate when the conveyor starts and stops
        self.start_event = simpy.Event(env)
        self.stop_event = simpy.Event(env)

        # start the conveyor processes
        env.process(self._start_stop())
       
        # starts the loop that waits for stuff
        # to be put onto the conveyor
        env.process(self._load_convayor())

    def _load_convayor(self):
        """
        Loads the conveyor from the load hopper

        After a package is placed on the convey
        it will wait for the package to move
        down the conveyor to make enough room
        for the next package.
        """
        while True:
            pack = yield self.load_hopper.get()

            # starts the package moving down the conveyor
            env.process(self._move_pack(pack))
            print(f"{env.now:.2f} pack {pack.pack_id} on convayor")
           
            # wait for space befor placing the next package
            remaining_wait_time = convey_load_time
            while remaining_wait_time > 0:
                last_start = self.env.now
                wait_event =  env.timeout(remaining_wait_time)

                # also stops when conveyor stops
                triggered = yield env.any_of([wait_event, self.stop_event])

                if wait_event in triggered:
                    # space is open for another package
                    remaining_wait_time = 0
                else:
                    # conveyor stopped, wait for start
                    remaining_wait_time -= self.env.now - last_start
                    yield self.start_event

    def _move_pack(self, pack):
        """
        Moves a package down the conveyor

        When a package gets to the end of the
        conveyor, it stops the conveyor
        """
       
        remaining_travel_time = convey_time

        while remaining_travel_time > 0:
            last_start = self.env.now

            travle_timeout = env.timeout(remaining_travel_time)

            # package also stops moving if conveyor stops
            triggered = yield env.any_of([travle_timeout, self.stop_event])

            if travle_timeout in triggered:
                # at end of conveyor
                remaining_travel_time = 0
           
            else:
                # conveyor has stopped, wait for restart
                remaining_travel_time -= self.env.now - last_start
                yield self.start_event

        # at end of conveyor, stop conveyor
        self.stop_event.succeed()
        self.stop_event = simpy.Event(self.env)
        yield self.pick_up_pile.put(pack)

        print(f"{env.now:.2f} pack {pack.pack_id} end of convayor")

    def pick_up(self):
        """
        Removes a package from the end of the conveyor
        """

        pack = yield self.pick_up_pile.get()

        # restart conveyor
        self.start_event.succeed()
        self.start_event = simpy.Event(self.env)

        return pack
   

    def queue_package(self, pack):
        """
        Puts a package into the queue to
        be conveyed on the conveyor
        """
        self.load_hopper.put(pack)
        print(f"{env.now:.2f} package {pack.pack_id} is queued")
   
    def _start_stop(self):
        """
        logs when the conveyor starts or stops
        """
        while True:
            yield self.stop_event
            print(f"{env.now:.2f} conveyor has stopped")

            yield self.start_event
            print(f"{env.now:.2f} conveyor has started")

def unload_conveyor(env, conveyor):
    """
    Unload the conveyor

    Unloader takes some time to process a package
    so there is a chance a package will sit at the
    end of the conveyor and block everything for
    a short time.
    """

    while True:
        print(f"{env.now:.2f} unloader waiting for a package")
        pack = yield env.process(conveyor.pick_up())
        print(f"{env.now:.2f} pack {pack.pack_id} unloaded from conveyor")

        # process the package
        yield env.timeout(random.triangular(1,6))


def package_arrivals(env, conveyor):
    """
    queues packages for the conveyor

    tries to show when convey is at compacity.

    If queue is empty a added package is
    immeditaly placed onthe conveyor.

    If queue is not empty, new packages
    wait their turn to be put on the conveyor
    """

    # prime the queue with a full load
    for i in range(15):
        conveyor.queue_package(Package())

    yield env.timeout(60)

    for i in range(4):
        conveyor.queue_package(Package())

    yield env.timeout(5)

    for i in range(4):
        conveyor.queue_package(Package())

env = simpy.Environment()

conveyor = Conveyor(env)

env.process(unload_conveyor(env, conveyor))

# make stream of packages
env.process(package_arrivals(env, conveyor))

env.run(200)

David García

unread,
Nov 5, 2023, 2:44:21 PM11/5/23
to python-simpy
I am very impressed with both examples!. Thank you very much!

In this second model, is it possible if destination is full, to move the package to another conveyor?

thank you!

Michael Gibbs

unread,
Nov 5, 2023, 2:51:46 PM11/5/23
to python-simpy
you can make the output store of one conveyor the input store of the next conveyor.
Reply all
Reply to author
Forward
0 new messages