Reputation: 3
i have a project and i am trying to simulate a RMFS warehouse with simpy. i would really appreciate it anyone can help me as i'm new to simpy and stuck and i dont know how to model it. i tried different codes but none of them work as they should. here is my assumptions: 1- the Warehouse is a grid and each cell contain many quantity of 1 type of item(item 1,2,3,...).something like the attached photo. it have a unique address 2- orders come in a predefined time (like with uniform distribution of 2 minutes) 3- the orders then is assigned to a robot (we have 10 robots) to go and retrieve the order. it will take 3 minutes 4- then the robot deliver the order to a workstation for next step (like pick the order from robot and package it). it will take 2 minutes. 5- then the robot go and put the order back and wait for next order.
Upvotes: 0
Views: 665
Reputation: 3
this is the update for the code considering a 6*9 Warehouse. i also added a lifting mechanism to it. i appreciate your comments on how we can improve and optimize it. and how to add reporting to it.
"""
modes a robot warehouse where robots get pulls a cell
and brings it to a picker who picks stuff from the cell,
then returns the cell back to the warehouse
basic process:
order arrives
order gets a robot (wait in a queue if all robots are in use)
order gets cell (wait if cell has been seized by another order)
pick from cell
put cell back
release robot
programmer: Michael R. Gibbs
"""
import simpy
import random
random.seed(0)
NUM_ROBOTS = 10
NUM_PICKERS = 1
NUM_GANTRY = 2
EXT_CELLS = [1,2,3,4,5,6,7,12,13,18,19,24,25,30,31,36,37,42,43,48,49,50,51,52,53,54]
INT_CELLS = [8,9,10,11,14,15,16,17,20,21,22,23,26,27,28,29,32,33,34,35,38,39,40,41,44,45,46,47]
class Order():
"""
Order to be fulfilled
has unique id for tracking
and the cell id to pull
"""
# class var used to gen unique ids
next_id = 1
def __init__(self, cell):
"""
initalizes a order with a unique id and a cell to pull
"""
# set unique id
self.id = Order.next_id
Order.next_id += 1
# set cell to pull
self.cell = cell
class Cell():
"""
Cell that robots retrieve so pickers can pick
A cell can only be seized by one robot at a time
so each cell has its own request queue implemented
with a simple resource.
"""
def __init__(self, env, id):
"""
Initializes a cell with a id, and a request/resource queue
"""
self.id = id
self.env = env
# used to queue requests for the cell
self.resQueue = simpy.Resource(env, capacity=1)
# request that currently holds the cell's resource
self.request = None
def seize(self):
"""
gets in queue and waits to seize cell
"""
request = self.resQueue.request() # Generate a request event
yield request
# save the request that has the cell so can release latter
self.request = request
return self
def release(self):
"""
releases the cell so other robots can seize it
"""
yield self.resQueue.release(self.request)
self.request = None
def gen_orders(env, cellMap, robots):
"""
Generates orders at a random distribution
and kicks off the fill order process for the order
"""
while True:
# time between arrivals
yield env.timeout(random.expovariate(1.0/1.5))
# create order and assign cell
cell = random.randint(1, len(cellMap))
order = Order(cell)
print('{:.2f} Order {} received for item in cell #{}'.format(env.now, order.id,order.cell))
# start process to fulfill the order
# do not use yield here, just drop and go to next order
if cell in EXT_CELLS:
env.process(fill_order(order, cellMap, robots, pickers))
else:
env.process(fill_order_internal(order, cellMap, robots, pickers,gantry))
def fill_order(order, cellMap, robots, pickers):
"""
the order filling process
this process gets created for each order
"""
#indicate cell status
print(format(env.now,'.2f'), f'order {order.id} cell {order.cell} is an external cell')
# get a robot
print('{:.2f} order {} waits for robot'.format(env.now,order.id))
with robots.request() as req:
yield req
print('{:.2f} order {} assigned to robot# {}'.format(env.now, order.id, robots.count))
# get the cell
print(format(env.now, '.2f'), f'order {order.id} waits for cell {order.cell}')
cell = cellMap[order.cell]
yield env.process(cell.seize())
print(format(env.now, '.2f'), f'order {order.id} has seized cell {order.cell}')
# pull the cell
yield env.timeout(3)
print(format(env.now,'.2f'), f'order {order.id} has pulled cell {order.cell} by robot#{robots.count}')
# pick
with pickers.request() as picker_req:
yield picker_req
yield env.timeout(random.triangular(0.5,1.2,1.8))
print(format(env.now,'.2f'), f'order {order.id} has picked')
# return cell
yield env.timeout(3)
env.process(cell.release())
print(format(env.now,'.2f'), f'order {order.id} has return cell {order.cell} by robot# {robots.count}')
# release robot
print('{:.2f} order {} has released a robot'.format(env.now,order.id))
def fill_order_internal(order, cellMap, robots, pickers,gantry):
"""
the order filling process for internal cells
this process gets created for each order
"""
#indicate cell status
print(format(env.now,'.2f'), f'order {order.id} cell {order.cell} is an internal cell')
# get a robot
print('{:.2f} order {} waits for robot and gantry'.format(env.now,order.id))
with robots.request() as req:
yield req
print('{:.2f} order {} assigned to robot# {}'.format(env.now, order.id, robots.count))
# get the cell
print(format(env.now, '.2f'), f'order {order.id} waits for cell {order.cell}')
cell = cellMap[order.cell]
yield env.process(cell.seize())
print(format(env.now, '.2f'), f'order {order.id} has seized cell {order.cell}')
# get the gantry
with gantry.request() as req_gantry:
yield req_gantry
print('{:.2f} order {} assigned to gantry# {}'.format(env.now, order.id, gantry.count))
#lift obstacle cells
yield env.timeout(2)
print(format(env.now, '.2f'), f'order {order.id} has lifted obstacles of cell {order.cell} by gantry{gantry.count}')
# pull the cell
yield env.timeout(3)
print(format(env.now,'.2f'), f'order {order.id} has pulled cell {order.cell} by robot#{robots.count}')
# pick
with pickers.request() as picker_req:
yield picker_req
yield env.timeout(random.triangular(0.5,1.2,1.8))
print(format(env.now, '.2f'), f'order {order.id} has picked')
# get the gantry
with gantry.request() as req_gantry:
yield req_gantry
print('at {:.2f} order {} assigned to gantry# {}'.format(env.now, order.id, gantry.count))
# lift obstacle cells for return
yield env.timeout(2)
print(format(env.now, '.2f'), f'order {order.id} has lifted obstacles of cell {order.cell} by gantry{gantry.count}')
# return cell
yield env.timeout(3)
env.process(cell.release())
print(format(env.now,'.2f'), f'order {order.id} has return cell {order.cell} by robot# {robots.count}')
# release robot
print('at {:.2f} order {} has released a robot'.format(env.now,order.id))
# start building the sim
env = simpy.Environment()
# 54 cells for a 6*9 Warehouse
cellMap = {id: Cell(env, id) for id in range(1, 55)}
#print(cellMap)
robots = simpy.Resource(env, capacity= NUM_ROBOTS)
pickers = simpy.Resource(env, capacity= NUM_PICKERS)
gantry = simpy.Resource(env, capacity= NUM_GANTRY)
# start generating orders, which also kicks off the processing for each order
env.process(gen_orders(env, cellMap, robots))
env.run(100)
Upvotes: 0
Reputation: 1969
gave it a shot, the process I modeled is
basic process:
order arrives,
order gets a robot (wait in a queue if all robots are in use),
order gets cell (wait if cell has been seized by another order),
pick from cell,
put cell back,
release robot
so rather then have the cells be a pool of resources (that would need a matching function to get the desired cell), each cell has its own resource pool of capacity of 1. I use this to queue requests for the cell and all the cells are in a dict for easy access
in hind sight, I think I could get better throughput if I had the order seize the cell before seizing the robot.
"""
modes a robot warehouse where robots get pulls a cell
and brings it to a picker who picks stuff from the cell,
then returns the cell back to the warehouse
basic process:
order arrives
order gets a robot (wait in a queue if all robots are in use)
order gets cell (wait if cell has been seized by another order)
pick from cell
put cell back
release robot
programmer: Michael R. Gibbs
"""
import simpy
import random
class Order():
"""
Order to be fulfilled
has unique id for tracking
and the cell id to pull
"""
# class var used to gen unique ids
next_id = 1
def __init__(self, cell):
"""
initalizes a order with a unique id and a cell to pull
"""
# set unique id
self.id = Order.next_id
Order.next_id += 1
# set cell to pull
self.cell = cell
class Cell():
"""
Cell that robots retrieve so pickers can pick
A cell can only be seized by one robot at a time
so each cell has its own request queue implemented
with a simple resouce. Could have put all the cells in
one matching store, but I think this is more effecient
as the matching can be slow with big queues
"""
def __init__(self, env, id):
"""
Initializes a cell with a id, and a request/resouce queue
"""
self.id = id
self.env = env
# used to queue requests for the cell
self.resQueue = simpy.Resource(env, capacity=1)
# request that currently holds the cell's resource
self.request = None
def seize(self):
"""
gets in queue and waits to seize cell
"""
request = self.resQueue.request() # Generate a request event
yield request
# save the request that has the cell so can release latter
self.request = request
return self
def release(self):
"""
releases the cell so other robots can seize it
"""
yield self.resQueue.release(self.request)
self.request = None
def gen_orders(env, cellMap, robots):
"""
Generates orders at a random distrubution
and kicks off the fill order process for the order
"""
while True:
# time between arrivals
yield env.timeout(random.uniform(0,2))
# create order and assign cell
cell = random.randint(1,len(cellMap))
order = Order(cell)
print(env.now, f'Order {order.id} has been created')
# start process to fulfill the order
# do not use yield here, just drop and go to next order
env.process(fill_order(order,cellMap, robots))
def fill_order(order, cellMap, robots):
"""
the order filling process
this process gets created for each order
"""
# get a robot
print(env.now, f'order {order.id} waits for robot')
with robots.request() as req:
yield req
print(env.now, f'order {order.id} has a robot')
# get the cell
print(env.now, f'order {order.id} waits for cell {order.cell}')
cell = cellMap[order.cell]
yield env.process(cell.seize())
print(env.now, f'order {order.id} has seized cell {order.cell}')
# pull the cell
yield env.timeout(3)
print(env.now, f'order {order.id} has pulled cell {order.cell}')
# pick
yield env.timeout(2)
print(env.now, f'order {order.id} has picked')
# return cell
yield env.timeout(3)
env.process(cell.release())
print(env.now, f'order {order.id} has return cell {order.cell}')
# release robot
print(env.now, f'order {order.id} has released a robot')
# start building the sim
env = simpy.Environment()
# made only 10 cells so orders are more likely to compete for a cell
cellMap = {id:Cell(env,id) for id in range(1,10)}
robots = simpy.Resource(env,capacity=10)
# start generating orders, which also kicks off the processing for each order
env.process(gen_orders(env, cellMap, robots))
env.run(100)
Upvotes: 0