本文算是对这段时间的分拨建模仿真工作的一个概要梳理。
目前国内的仿真相关的工作并没有像国外那么热门。国内大部分需要用到仿真的时候,都会将仿真任务外包到一些专门做“仿真”的公司来做,当然这么做的后果就是会导致建模仿真的不够专业,仿真结果不能令人满意。
做了快 1 年的仿真工作,有这么点感触:“想成为顶尖的仿真专业人士,非常困难。首先你需要扎实的计算机数学基础。其二需要你有非常专业的相关领域的业务知识。最重要的是具有能将仿真与实际场景有机结合起来,进行闭环操盘的能力”。大部分人都能做到第一点,少部分人能做到第二点,几乎没有人能做到第三点。
以上 瞎扯
以下 正文
要对一个分拨(不管是仓库也好,车站也好)个人以为首先要做的工作就是梳理清楚这个 _系统_ 的组成部分以及是如何工作的(这很重要,如果你不清楚,那基本上是没得玩的)。再了解清楚系统的组成和如何工作之后,接下来要做的就是知道如何用仿真工具搭建一个分拨模型并 run 这个模型。最后将仿真结果应用于实际。
下面就以了解系统组成和如何工作到如何使用仿真工具进行建模来简要总结一下。
分拨模块
- 车类型
站点车
网络车(普通车,大挂车) - 件类型
散件
包
子单 - 卸货口
卸货类型(站点件/网络件)
卸货效率 - 称重岗(站点件称重)
称重效率
- 粗分岗
粗分方向
粗分效率 - 主线
主线方向
主线耗时 - 拆包岗
拆包效率
- 手工集包岗
集包方向
集包单位 - 自动集包
集包方向
集包单位 - 细分岗
细分方向
细分效率 - 扫描岗
扫描效率
杭州分拨省内库实体流向图
如上图就是杭州分拨省内库的主要流向图。
更具体的流程应该如下:
卸货口选择流程
称重扫描岗位选择流程
如果是站点件都会进行称重操作。
拆包平台选择(拆包平台的选择,每个分拨多少都会有些许出入)
如果是市内方向则将包裹流向自动线方向的拆包区。如果是省内方向活着市郊方向的件则将把流向三层分拣方向的拆包平台。
集包线/集包柜选择流程
集包平台种类比较多 自动集包线,手工集包线(八爪鱼集包)集包柜等。
无论是哪种集包平台,都是根据集包方向集包单位进行集包的。
将集好的包流向主线。
细分岗选择流程
每一个细分岗对应一个方向,分拣对应发件方向的件。
分拨模型建模
组件概览:
杭州分拨模型图
分拨一楼模型:
分拨二楼模型:
配置文件生成平台
目前大部分使用到的仿真组件都已经能使用仿真文件生成平台进行生成。
概要
该工程主要实现一个将分拨模块转化成仿真引擎可执行的配置文件的工具。
目前已经将部分仿真分拨时会用到的仿真模型中的组件进行模块到组件的映射。
注意:
本工程使用到 Java8 的一些特性,所以在开发和运行该工程的时候,请确保开发环境是 JDK 8+。
目录概览
.src
|____test 单元测试目录
| |____java
| | |____com
| | | |____runningapple
| | | | |____simulation
| | | | | |____basicsim
| | | | | |____processflow
| | | | | | |____UnpackTest.java
| | | | | | |____EntityConveyorTest.java
| | | | | | |____RemoveFromTest.java
| | | | | | |____EntityContainerTest.java
| | | | | | |____BranchTest.java
| | | | | | |____EntitySinkTest.java
| | | | | | |____AssignTest.java
| | | | | | |____EntityGeneratorTest.java
| | | | | | |____GeneratorTest.java
| | | | | | |____PackTest.java
| | | | | | |____SimEntityTest.java
| | | | | | |____EntityDelayTest.java
| | | | | | |____ServerTest.java
| | | | | | |____AddToTest.java
| | | | | |____utils
| | | | | | |____UtilsTest.java
| | | | | | |____DBTest.java
| | | | | |____basicobjects
|____main 源文件目录
| |____resources 配置文件目录
| | |____Head.cfg 仿真文件模板
| |____java
| | |____com
| | | |____runningapple
| | | | |____real
| | | | | |____enums
| | | | | | |____CompType.java 模型类型枚举类
| | | | | |____basic
| | | | | | |____Pak.java
| | | | | | |____NextComp.java
| | | | | | |____BaseComp.java
| | | | |____simulation
| | | | | |____basicsim
| | | | | | |____Component.java 仿真组件基类
| | | | | |____processflow
| | | | | | |____EntityGenerator.java 组件生成器
| | | | | | |____Unpack.java 拆包组件
| | | | | | |____EntitySink.java 结束组件(一个模型中至少有一个结束组件,用来结束临时实体的生命周期)
| | | | | | |____Assign.java 赋值组件
| | | | | | |____SimEntity.java 临时实体
| | | | | | |____EntityContainer.java 临时实体容器
| | | | | | |____RemoveFrom.java 匹配拆包组件
| | | | | | |____Branch.java 分拣组件
| | | | | | |____EntityDelay.java 延时组件
| | | | | | |____Server.java 服务组件
| | | | | | |____Que.java 队列组件
| | | | | | |____EntityConveyor.java 传输带组件
| | | | | | |____AddTo.java 匹配集包组件
| | | | | | |____Pack.java 自动集包组件
| | | | | |____utils
| | | | | | |____DBLink.java JDBC 数据库连接工具
| | | | | | |____Utils.java 普通文件写出工具
| | | | | | |____Generator.java 组件生成器
组件生成算法
对每一个分拨操作模块进行深度优先遍历,根据每一种模块生成对应对仿真组件,大部分分拨操作模块和仿真组件都不是 1 对 1 的关系,
比如一个分拣模块对应的仿真组件就是 1 个服务组件 + 1 个队列组件 + 1 个分拣组件。
在生成每一个组件的同时,会以 key-组件 形式存储当前生成的组件。这样做的目的主要是为了防止组件重复生成,并且在生成一个组件之前都会去验证一下当前这个即将要生成的组件是否已经生成过,如果已经生成过就直接拿已经生成的组件过来使用。
配置文件则是以广度优先遍历每一个仿真组件,在生成组件的同时,会标记一下当前组件是否遍历过,如果遍历过就不再遍历。
其它
临时实体在仿真模型中大部分都是以 _包_ 形式存在的,只有在 _拆包和集包_ 时候会以 _子单_ 形式存在。
在仿真组件中 装包和拆包 有两种模式,装包分别是*匹配装包*和*自动装包*。
匹配装包(AddTo):如果要将一个包过下的子单装到其包裹里,就需要用到匹配装包组件。
自动装包(Pack):自动装包组件可以用在将包裹按固定个数随机打包的场景,比如集包线。
匹配装包和自动装包也有各自对应的拆包组件。
AddTo ---》 RemoveFrom
Pack ---》 Unpack
预处理相关
如何解决零临时实体种类过多,创建时无法判断应该创建哪些?
将所有临时实体都创建(即都在模型里给予一个对应的临时实体生成器)
包与子单的处理
不管创建何种分拨模型,在预处理的时候都会将子单装配到对应的包(匹配装包)里。当然这么做可能会造成仿真运行效率低下,但是没办法,这是妥协之策。
另外包和子单在进入模型后都会先进行赋值操作。
散件的处理
散件是指小于 3kg 的散件,非小于 3kg 的散件会被当作包进入系统(区分散件和包的工作是在 SQL 里处理)
散件进入模型后会进行随机装包处理(20 个散件装 1 个包)
卸货口的选择
卸货口种类会有多种 站点卸货,网络卸货,挂车卸货等,加之还需要考虑到卸货口队列长度,所以在选择卸货口的时候需要动态考虑这些因素,
特别是卸货口关闭开启的情况,有些卸货口可能白天的时候是关闭的,晚上是开启的。
关于用户手册使用
可以阅读 JaamSim User Manual 2017-10.pdf 来了解相关仿真组件的作用和使用。
可以阅读 JaamSim Programming Manual - rev 0.51.pdf 来了解仿真引擎一些具体细节如何实现的,如条件等待,开始一个流程,结束一个事件等。