【问题标题】:Simpy: An RMFS simulation with SimpySimpy:使用 Simpy 进行 RMFS 模拟
【发布时间】:2021-06-24 17:27:08
【问题描述】:

我有一个项目,我正在尝试用 simpy 模拟一个 RMFS 仓库。我真的很感激任何人都可以帮助我,因为我是 simpy 新手并且卡住了,我不知道如何建模。我尝试了不同的代码,但没有一个可以正常工作。这是我的假设: 1- 仓库是一个网格,每个单元格包含许多数量的 1 种类型的项目(项目 1、2、3、...)。类似于附图。它有一个唯一的地址 2- 订单在预定义的时间内出现(例如 2 分钟的均匀分布) 3- 然后将订单分配给一个机器人(我们有 10 个机器人)去取回订单。需要 3 分钟 4-然后机器人将订单交付到工作站以进行下一步(例如从机器人中挑选订单并打包)。这将需要 2 分钟。 5- 然后机器人去放回订单并等待下一个订单。

【问题讨论】:

  • 你走了多远?我假设每个细胞只能被一个机器人抓住(这将是棘手的部分)?有代码吗?

标签: simulation simpy


【解决方案1】:

试一试,我建模的过程是

基本流程:

  • 订单到了,

  • 订单获取机器人(如果所有机器人都在使用中,则排队等待),

  • 订单获取单元格(等待单元格已被另一个订单占用),

  • 从单元格中挑选,

  • 把单元格放回去,

  • 释放机器人

因此,与其让单元成为一个资源池(这将需要一个匹配函数来获得所需的单元),每个单元都有自己的容量为 1 的资源池。我使用它来对单元的请求进行排队,并所有单元格都在一个字典中以便于访问

事后看来,如果我让命令在抢占机器人之前抢占牢房,我想我可以获得更好的吞吐量。

"""
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)

【讨论】:

  • 你的假设是正确的。你的代码很棒。问题是 1)我们需要在选择器前面有一个队列(我们可以将其视为具有 1 个容量的资源),主要部分 2)是单元格的布局。它们有一个网格布局(就像我附在第一篇文章中的照片一样),它们有两种单元格(内部单元格和外部单元格)。外部单元可以由 1 个机器人拉动,但对于内部单元,我正在考虑引入另一种资源(如电梯)来拾取障碍单元,以便机器人可以拉动订单单元。请让我知道您的想法以及我该怎么做。谢谢
  • 我正在努力将我提到的内容添加到代码中,一旦完成,我将在此处发布。如果你能同时解决它,请告诉我。非常感谢您的帮助
  • 也请检查citi-ntust.squarespace.com/en/research/kiva。我试图以高密度模拟这个 RMFS
  • 选择器不要太用力。我同意选择器可以是容量为 1 的资源的模型。这也允许您通过将容量增加到 2 来模拟 2 个选择器。只需在选择器超时为的 fill_order 过程中创建一个选择器过程
  • 我没有读过这篇文章,但是,我认为机器人会将外部单元格移动到“保持”点,假设单元格没有被另一个机器人拉动,然后机器人会拉动目标细胞。当机器人返回单元格时,它必须检查外部单元格是否在保持点(可能已被另一个机器人拉动),如果它在保持点,则返回单元格的原点。这意味着他的单元格类需要一个内部单元格标志、外部单元格 ID 和保持点标志。显然还有很多小细节,但这涵盖了主要逻辑
【解决方案2】:

这是考虑到 6*9 仓库的代码更新。我还为它添加了一个提升机制。我感谢您的 cmets 关于我们如何改进和优化它。以及如何向其添加报告。

"""
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)

【讨论】:

  • 我唯一要做的就是添加一个 is_internal Cell 属性,类似于 id 属性。使用此属性,您可以使用 if 语句(如果 cell.is_internal:) 将获取 fill_order_internal 的龙门部分装箱,这样代码将适用于两种类型的 Cell,因此您只需要一个函数
  • 您可能已经注意到 simpy 没有内置报告,但是您可以使用所有 python 来弥补这一点。您的选择是:1)将您的数据记录到一个文件并在另一个应用程序中处理它

    2)在全局列表和数据框中收集数据,并在模拟结束时以 matplotlib 之类的方式显示它

    3)将 sim 变成一个多线程应用程序,在一个线程上更新显示,并在另一个线程上运行 sim。(有点复杂,但对于长时间运行的 sim 来说更有趣)

  • 谢谢,@Michael。你的cmets很棒。我真的很感激他们。
  • 谢谢,你有没有机会选择我的答案为正确的,或者记下我的一些 cmets?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-14
  • 2017-07-12
  • 1970-01-01
相关资源
最近更新 更多