Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Visualization of a discrete-event simulation on a grid / warehouse layout

I need to simulate a warehouse with several autonomous vehicles moving on a given layout with simple priority rules. In my understanding, the problem can easily be tackled with discrete-event simulation (DES) and I would use SimPy for this.

The problem I see is that it seems very hard to me to visualize the actual trajectories and interactions of these vehicles. Of course, I can log all positions of all vehicles in all periods but how can I proceed then to create a visualization?

The dumbest way would be to create a million pictures, but there has to be a better way. Is there any library or tool to visualize movements of objects on a grid by moving symbols before a background?

Another option would be to use an agent-based approach with a software like AnyLogic, but this seems more complicated to me and I would like to apply a DES approach, preferably with open-source software.

like image 503
mondano Avatar asked Apr 24 '17 07:04

mondano


2 Answers

I'd suggest checking out the tkinter library. We do all of our simpy visualization using this.

Here is a very basic example of the kind of animation that can be achieved, pardon the dramatic footage: https://www.youtube.com/watch?v=xnZQ0f--Ink

Here is the source code which roughly describes what you see above: https://github.com/harrymunro/Simulations/blob/master/termini_simulation_animation.py

Here is a copy paste of the animation component:

################ SET UP ANIMATION CANVAS #################
class Train:
    def __init__(self, canvas, x1, y1, x2, y2, tag):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2
        self.canvas = canvas
        self.train = canvas.create_rectangle(self.x1, self.y1, self.x2, self.y2, fill="red", tags = tag)
        self.train_number = canvas.create_text(((self.x2 - self.x1)/2 + self.x1), ((self.y2 - self.y1)/2 + self.y1), text = tag)
        self.canvas.update()

    def move_train(self, deltax, deltay):
        self.canvas.move(self.train, deltax, deltay)
        self.canvas.move(self.train_number, deltax, deltay)
        self.canvas.update()

    def remove_train(self):
        self.canvas.delete(self.train)
        self.canvas.delete(self.train_number)
        self.canvas.update()

class Clock:
    def __init__(self, canvas, x1, y1, x2, y2, tag):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2
        self.canvas = canvas
        self.train = canvas.create_rectangle(self.x1, self.y1, self.x2, self.y2, fill="#fff")
        self.time = canvas.create_text(((self.x2 - self.x1)/2 + self.x1), ((self.y2 - self.y1)/2 + self.y1), text = "Time = "+str(tag)+"s")
        self.canvas.update()

    def tick(self, tag):
        self.canvas.delete(self.time)
        self.time = canvas.create_text(((self.x2 - self.x1)/2 + self.x1), ((self.y2 - self.y1)/2 + self.y1), text = "Time = "+str(tag)+"s")
        self.canvas.update()


if show_animation == True:
    animation = Tk()
    #bitmap = BitmapImage(file="uxbridge.bmp")

    im = PhotoImage(file="uxbridge_resized.gif")

    canvas = Canvas(animation, width = 800, height = 400)
    canvas.create_image(0,0, anchor=NW, image=im)
    animation.title("Uxbridge Termini Simulation")

    canvas.pack()

#### matplotlib plots


if show_animation == True and hide_plots == False:
    f = plt.Figure(figsize=(5,4), dpi=100)

    a1 = f.add_subplot(221) # mean headway
    a2 = f.add_subplot(222) # TPH meter
    a3 = f.add_subplot(223) # headway distribution
    a4 = f.add_subplot(224) # train count

    a1.plot()
    a2.plot()
    a3.plot()
    a4.plot()

    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg

    dataPlot = FigureCanvasTkAgg(f, master=animation)
    dataPlot.show()
    dataPlot.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
    f.tight_layout()

    canvas.pack()

# platforms
if show_animation == True:
    canvas.create_rectangle(50, 100, 200, 150, fill = "yellow")
    canvas.create_rectangle(50, 200, 200, 250, fill = "yellow")

    canvas.create_line(50, 75, 200, 75, fill="green", width=3) # platform 4
    canvas.create_line(50, 175, 200, 175, fill="green", width=3) # platform 2/3
    canvas.create_line(50, 275, 200, 275, fill="green", width=3) # platform 1

    canvas.create_text(125, 110, text = "Platform 4")
    canvas.create_text(125, 140, text = "Platform 3")
    canvas.create_text(125, 210, text = "Platform 2")
    canvas.create_text(125, 240, text = "Platform 1")

# track
    canvas.create_line(200, 75, 650, 75, fill="green", width=3) # platform 4 run out
    canvas.create_line(200, 175, 650, 175, fill="green", width=3) # platform 2/3 run in
    canvas.create_line(300, 175, 400, 75, fill="green", width=3)
    canvas.create_line(450, 75, 600, 175, fill="green", width=3)
    canvas.create_line(450, 175, 600, 75, fill="green", width=3)
    canvas.create_line(200, 275, 300, 275, fill="green", width=3)
    canvas.create_line(300, 275, 400, 175, fill="green", width=3)

############ END OF CANVAS #################
like image 166
bobo Avatar answered Nov 01 '22 09:11

bobo


If the animation should be 2D you could use the Pygame Library. I animated a little simpy simulation with it and it worked fine. Just note that you need to use threads otherwise your window will freeze after a few seconds. This simple method draws a red circle for every customer arriving and draws it green when the customer gets served.

def draw(env, timelist):
    gameDisplay.fill(white)
    start = time.clock()
    kdnr = 0
    kdaktuell = -1
    kdstart = -10
    while True:
        timer = (time.clock() - startzeit)
        if timer > 15: #simulation for 15 sec
                break

    # incoming customers
    if kdnr < len(timelist):
        if timelist[kdnr] <= timer:
            pygame.draw.circle(gameDisplay,red,(50+30*kdnr,400),10)
            print('Customer '+str(kdnr+1)+ ' arrived in minute: ' + str(timelist[kdnr]))
            kdnr = kdnr + 1

    # served customers
    if (kdstart+3) <= timer:
        kdaktuell = kdaktuell + 1
        kdstart = time
        pygame.draw.circle(gameDisplay,green,(50+30*kdaktuell,400),10)
        print('Customer '+str(kdaktuell+1)+ ' gets served.')

    pygame.display.update()
like image 24
AUBSieGUL Avatar answered Nov 01 '22 08:11

AUBSieGUL