[Pyhton] SimPy 离散事件模拟框架详解 —— 以一个简单的汽车充电排队模拟为例
一、背景知识
人们在生产活动和社会活动中,经常遇到一类复杂的系统,这类系统中有许多事件时而出现,时而消失,时而动作,时而停止,而启动和停止都发生在一些离散的时刻,并带有一定的随机性。例如,港口中船舶的停靠码头、生产线上机床的启停、电话的接通和断开、计算机系统中某项作业的进行和退出,凡此种种,都带有上述特点,这类系统叫做离散事件动态系统(DEDS)。随着生产和科技的日益发展,以及人类社会交往的日趋频繁,这类系统的数量和种类也越来越多。
设计此类系统时,往往需要仿真来评估算法或方案的性能,常见的软件有:
商业软件:
| 名字 | 简介 | 图示 |
|---|---|---|
| AnyLogic | 通用多方法建模工具。结合了基于代理、系统动力学和离散事件建模。 | |
| Arena | 一种离散事件模拟程序,也允许对连续过程进行建模。 | |
| Care pathway simulator | 专门为服务行业(如医疗保健)设计的离散事件模拟程序。 | |
| Enterprise Dynamics | 一个模拟软件平台,用于模拟和分析几乎任何制造、材料处理和物流挑战。 | |
| ExtendSim | 通用仿真软件包 | |
| DELMIA | 3DEXPERIENCE 平台的一部分 | |
| FlexSim | 拖拖拉拉做离散事件模拟,3D | |
| GoldSim | 将动态离散事件模拟嵌入到 Monte Carlo 框架 | |
| GPSS | 离散事件模拟语言。供应商可以提供不同的实现 | |
| Micro Saint Sharp | 通用离散事件建模工具,使用拖放界面和C#编程语言 | |
| MS4 Modeling Environment | 基于离散事件和混合模型的通用DEVS方法的软件环境 | |
| Plant Simulation | 能够模拟和优化生产系统和流程的软件 | |
| ProModel AutoCAD Edition | 在数字孪生Autodesk工具集(AutoCAD和Inventor)中快速构建空间精确的布局和过程仿真模型。 | |
| Simcad Pro | 实时变化,零代码有界面,支持 VR | |
| SimEvents | 向MATLAB / Simulink环境添加离散事件仿真。 | |
| SIMUL8 | 基于对象的仿真软件 | |
| VisualSim | 基于时序、功耗和功能的电子、嵌入式软件和半导体的基于模型的系统架构探索 | |
| WITNESS | 可在桌面和云端使用 VR 进行离散事件模拟 |
开源软件:
| 名字 | 语言 | 类型 | License | 简介 |
|---|---|---|---|---|
| JaamSim | Java | App | Apasche 2.0 | JaamSim是一款自由开源的离散事件模拟软件,包括拖放式用户界面、交互式3D图形、输入和输出处理以及模型开发工具和编辑器。 |
| CPN Tools | BETA | App | GPLv2 | 用于分析所有类型应用中的物流/排队模型的工具。 |
| DESMO-J | Java | Lib | Apasche 2.0 | Java离散事件模拟框架,支持混合事件/过程模型,并提供2D和3D动画。 |
| Facsimile | Scala | Lib | LGPLv3 | 离散事件模拟/仿真库。 |
| PowerDEVS | C++ | App | AFL, GPLv2 | 基于DEVS形式的混合系统建模和仿真集成工具。 |
| Ptolemy II | Java | Lib | BSD | 支持面向角色设计实验的软件框架。 |
| SIM.JS | JavaScript | Lib | LGPL | JS是一个完全用JavaScript编写的通用离散事件模拟库。在浏览器中运行,支持基于GUI的建模工具。 |
| SimPy | Python | Lib | MIT | SimPy是基于标准Python的基于过程的离散事件模拟框架。 |
| Simula | Simula | Language | 一种专门为模拟而设计的编程语言。 | |
| SystemC | C++ | Lib | Apache 2.0 | 提供事件驱动模拟内核。 |
注:软件示意图见《附录二》
二、SimPy 讲解
2.1 SimPy 概述
1)SimPy 是 python 的离散事件模拟框架。
2)SimPy 中的流程由 Python 生成器函数定义。例如,可以用于为客户、车辆或代理等活动组件建模。SimPy 还提供各种类型的共享资源来模拟容量有限的拥塞点(如服务器、结账柜台和隧道)。
3)模拟可以要多快有多快、实时、或者手动步进三种方式进行。
3)尽管理论上 SimPy 可以用于连续模拟,但它没有任何功能来帮助您实现这一点。另一方面,SimPy 对于具有固定步长的模拟来说是过火的,在这种情况下,您的流程不会相互交互或共享资源。
模拟两个时钟在不同时间间隔滴答作响的简短示例如下:
>>> import simpy
>>>
>>> def clock(env, name, tick):
... while True:
... print(name, env.now)
... yield env.timeout(tick)
...
>>> env = simpy.Environment()
>>> env.process(clock(env, 'fast', 0.5))
<Process(clock) object at 0x...>
>>> env.process(clock(env, 'slow', 1))
<Process(clock) object at 0x...>
>>> env.run(until=2)
fast 0
slow 0
fast 0.5
slow 1
fast 1.0
fast 1.5
2.2 基本概念
1)SimPy 是一个离散事件仿真库。活动组件(如车辆、客户或消息)的行为是用流程建模的。所有进程都存在于一个环境中。它们通过事件与环境和彼此交互。
2)流程由简单的 Python generator 描述。你可以将他们称为过程函数或过程方法,取决于它是函数还是类的方法。在其整个生命周期内,他们产生事件等待被触发。
3)当一个过程产生一个事件时,该进程就会被挂起。当事件发生时(我们说事件被触发),SimPy 恢复该过程。多个进程可以等待同一个事件。SimPy 以它们产生该事件的相同顺序恢复它们。
4)一个最重要的事件类型就是 Timeout 类事件。它允许在进程给定的时间内休眠(或保持其他状态)。

2.3 一个汽车开开停停的例子
下面是一个简单的汽车走走停停的例子,打印其走停的时间戳:
>>> def car(env):
... while True:
... print('Start parking at %d' % env.now)
... parking_duration = 5
... yield env.timeout(parking_duration)
...
... print('Start driving at %d' % env.now)
... trip_duration = 2
... yield env.timeout(trip_duration)
>>> import simpy
>>> env = simpy.Environment()
>>> env.process(car(env))
<Process(car) object at 0x...>
>>> env.run(until=15)
Start parking at 0
Start driving at 5
Start parking at 7
Start driving at 12
Start parking at 14
2.4 在走走停停过程中增加充电过程(过程交互)
我们在上面汽车例子基础上引入充电的过程:车走一段时间,停下来充电,电充好了,才能继续走。
这里引入了 charge_duration 过程,在该过程中简单写了一个超过的挂起事件:
>>> class Car(object):
... def __init__(self, env):
... self.env = env
... # Start the run process everytime an instance is created.
... self.action = env.process(self.run())
...
... def run(self):
... while True:
... print('Start parking and charging at %d' % self.env.now)
... charge_duration = 5
... # We yield the process that process() returns
... # to wait for it to finish
... yield self.env.process(self.charge(charge_duration))
...
... # The charge process has finished and
... # we can start driving again.
... print('Start driving at %d' % self.env.now)
... trip_duration = 2
... yield self.env.timeout(trip_duration)
...
... def charge(self, duration):
... yield self.env.timeout(duration)
>>> import simpy
>>> env = simpy.Environment()
>>> car = Car(env)
>>> env.run(until=15)
Start parking and charging at 0
Start driving at 5
Start parking and charging at 7
Start driving at 12
Start parking and charging at 14
如果我们不想等充电结束,而是想中断充电过程并开始驾驶,可以使用 SimPy 的 interrupt() 方法来中断正在运行的进程:
>>> def driver(env, car):
... yield env.timeout(3)
... car.action.interrupt()
由于原来的充电过程被中断会报异常,因此我们要对异常处理下:
... try:
... yield self.env.process(self.charge(charge_duration))
... except simpy.Interrupt:
... # When we received an interrupt, we stop charging and
... # switch to the "driving" state
... print('Was interrupted. Hope, the battery is full enough ...')
再次运行:
>>> env = simpy.Environment()
>>> car = Car(env)
>>> env.process(driver(env, car))
<Process(driver) object at 0x...>
>>> env.run(until=15)
Start parking and charging at 0
Was interrupted. Hope, the battery is full enough ...
Start driving at 3
Start parking and charging at 5
Start driving at 10
Start parking and charging at 12
2.5 共享资源
SimPy 提供三种类型的资源,用于解决建模中多个进行希望使用有限资源的问题(例如:加油站汽车场景中的燃油泵)或典型的生产者-消费者问题。

我们还用汽车充电的例子:汽车开到充电桩旁 a battery charging station (BCS),向两个充电桩申请使用其一进行充电,如果两个桩都在被使用,它将会等待直到可用,然后开始充电,然后开走。
>>> def car(env, name, bcs, driving_time, charge_duration):
... # Simulate driving to the BCS
... yield env.timeout(driving_time)
...
... # Request one of its charging spots
... print('%s arriving at %d' % (name, env.now))
... with bcs.request() as req:
... yield req
...
... # Charge the battery
... print('%s starting to charge at %s' % (name, env.now))
... yield env.timeout(charge_duration)
... print('%s leaving the bcs at %s' % (name, env.now))
备注: bcs.request() 将会产生一个事件,该事件会阻塞直到资源可用,一般情况下使用资源后需要调用 release 对资源进行释放,这里的 with 语句意味着自动释放。
我们创建有两个充电桩的资源:
>>> import simpy
>>> env = simpy.Environment()
>>> bcs = simpy.Resource(env, capacity=2)
然后我们创建 4 辆车:
>>> for i in range(4):
... env.process(car(env, 'Car %d' % i, bcs, i*2, 5))
最后,我们可以开始模拟了。由于汽车进程在此模拟中都自行终止,因此我们无需指定直到时间——当没有更多事件时,模拟将自动停止:
>>> env.run()
Car 0 arriving at 0
Car 0 starting to charge at 0
Car 1 arriving at 2
Car 1 starting to charge at 2
Car 2 arriving at 4
Car 0 leaving the bcs at 5
Car 2 starting to charge at 5
Car 3 arriving at 6
Car 1 leaving the bcs at 7
Car 3 starting to charge at 7
Car 2 leaving the bcs at 10
Car 3 leaving the bcs at 12
注意到前两辆车到达BCS后可以立即开始充电,而2号车和3号车需要等待,符合预期。
三、后续
之后我将用 SimPy 模拟射频节点的数据收发,进一步做一个 MESH 通信的模拟程序,用于验证不同的算法对 MESH 网络带来的性能差异。
敬请期待!!!

注:BLUETOOTH MESH 是利用蓝牙广播链路,基于洪范算法做的一种简单的组网协议(这里可以将广播理解为喊话、UDP广播等)
参考链接
[1]. SimPy 主页
[2]. 百度百科离散事件动态系统
[3]. List of discrete event simulation software
[4]. 离散事件系统仿真(原书第5版)
: 如果觉得不错,帮忙点个支持哈~

附录二
AnyLogic:

Arena:

FlexSim:

GoldSim:

Plant Simulation:

ProModel AutoCAD Edition:

VisualSim:

[Pyhton] SimPy 离散事件模拟框架详解 —— 以一个简单的汽车充电排队模拟为例的更多相关文章
- 调用ocx ActiveX控件详解(做一个简单的ocx控件)
背景 最近做的项目都和插件有关,就是在页面中调用插件的方法,然后进行操作. 插件就是ocx ActiveX控件,具体的说明可以自己去了解一下,在这里就不做赘述. 具体调用方式很简单: 1.在页面中写一 ...
- jQuery 事件用法详解
jQuery 事件用法详解 目录 简介 实现原理 事件操作 绑定事件 解除事件 触发事件 事件委托 事件操作进阶 阻止默认事件 阻止事件传播 阻止事件向后执行 命名空间 自定义事件 事件队列 jque ...
- cocos2dx+lua注册事件函数详解
coocs2dx 版本 3.1.1 registerScriptTouchHandler 注册触屏事件 registerScriptTapHandler 注册点击事件 registerScriptHa ...
- cocos2dx+lua注册事件函数详解 事件
coocs2dx 版本 3.1.1 registerScriptTouchHandler 注册触屏事件 registerScriptTapHandler ...
- (转载)【cocos2dx 3.x Lua] 注册事件函数详解
出处: http://www.2cto.com/kf/201409/338235.html coocs2dx 版本 3.1.1 registerScriptTouchHandler 注册触屏事件 re ...
- 委托与事件代码详解与(Object sender,EventArgs e)详解
委托与事件代码详解 using System;using System.Collections.Generic;using System.Text; namespace @Delegate //自定义 ...
- 【转】关于cocos2dx+lua注册事件函数详解
转载:http://www.taikr.com/article/1605 registerScriptTouchHandler 注册触屏事件registerScriptTapHandler注册点击事件 ...
- (5)ps详解 (每周一个linux命令系列)
(5)ps详解 (每周一个linux命令系列) linux命令 ps详解 引言:今天的命令是用来看进程状态的ps命令 ps 我们先看man ps ps - report a snapshot of t ...
- (4)top详解 (每周一个linux命令系列)
(4)top详解 (每周一个linux命令系列) linux命令 top详解 引言:今天的命令是用来看cpu信息的top top 我们先看man top top - display Linux pro ...
随机推荐
- MySQL查询性能优化七种武器之链路追踪
MySQL优化器可以生成Explain执行计划,我们可以通过执行计划查看是否使用了索引,使用了哪种索引? 但是到底为什么会使用这个索引,我们却无从得知. 好在MySQL提供了一个好用的工具 - opt ...
- Flink介绍
1,简介 Flink是Apache基金会旗下的一个开源大数据处理框架.Flink很牛逼,好多牛逼的公司都在用. 2,特征 *高吞吐和低延迟.每秒处理百万个时间,毫秒级延迟.有点既要老婆好,又要彩礼少的 ...
- python(第四版阅读心得)(系统工具)(一)
本章将会讲解python常用系统工具的介绍 python中大多数系统级接口都集中在两个模块: sys 和 os 但仍有部分其他标准模块也属于这个领域 如: 常见: glob 用于文件名扩展 soc ...
- Typora多线程批量上传图片,永久免费25G图床
为了满足日常需求,就写了一个自动上传图片到图床的脚本 运行该程序可以做到自动完成图片上传,并自动替换为网络链接,支持多图同时上传,采用了多线程,上传速度提升很明显. 以Window系统为例,操作步骤: ...
- Linux之LVM逻辑卷管理
LVM逻辑卷管理 LVM机制:PV物理卷,VG卷组,LV逻辑卷. --功能-- --物理卷管理-- --卷组管理-- --逻辑卷管理-- create(建立) pvcreate vgcreate lv ...
- 简析XDP的重定向机制
GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. GreatSQL是MySQL的国产分支版本,使用上与MySQL一致. 一. XDP Socket示例解析 源码参见:htt ...
- KingbaseES 中select distinct on 语句
用法 SELECT DISTINCT ON ( expression [, ...] ) 把记录根据[, -]的值进行分组,分组之后仅返回每一组的第一行. 需要注意的是,如果不指定ORDER BY子句 ...
- KingbaseES集群部署工具安装
关键字: KingbaseES.Java.ClientTools 一.安装前准备 1.1 软件环境要求 金仓数据库管理系统KingbaseES V8.0支持微软Windows 7.Windows XP ...
- 对表白墙wxml文件解释
一.index.wxml 1.代码 1 <view class="Beijingse" style="height: 100%;"> 2 <v ...
- 【项目实战】用Pytorch实现线性回归
视频教程:https://www.bilibili.com/video/BV1Y7411d7Ys?p=5 准备数据 首先配置了环境变量,这里使用python3.9.7版本,在Anaconda下构建环境 ...