Plant Simulation 学习笔记(一)
- 1.问题描述
- 1.1 二次分配问题描述
- 2.创建新项目
- 2.1在模型中添加对象
- 2.2 添加全局变量
- 2.3 从至表数据处理
- 2.4 从至表数据校核
- 3. 仿真建模思路
- 4. 建立QAP模型
- 4.1 定义机器序列
- 4.2 定义零件加工顺序表
- 4.3 生成机器及其前置暂存区
- 4.4 调入Load和Leave
- 5. 布置设计的优化
- 5.1 设置GA
- 5.2 仿真结果
- 6. 思考
1.问题描述
(声明:本笔记参考《生产系统仿真:Plant Simulation应用教程》这本书)
1.1 二次分配问题描述
设备布局问题是典型的二次分配问题(Quadratic Assignment Problem,QAP):将n个设备放置到n个位置上,其中每个位置上能且仅能放置一台设备,共有n!个选择。有两种情况:
(1)已知n个设备(设施规划称为作业单位Activity或设施)两两之间的物料搬运量大小Wij(i=1,2,…,n, j=1,2,…,n),以及n个固定位置(设施规划中称为工作地),并且这n个工作地之间的距离为Dij。布置问题就是如何将n个作业单位分配到n个工作地,使得总的物流量为最小,即
(2)已知n个作业单位两两之间的物料搬运量大小Wij及作业单位的形状、面积,合理安排作业单位之间的关系,使全部的物流量为最小或接近最小值。这是典型的设施规划问题,例如,已知一间工厂的总平面图,以及工厂将要生产的产品、工艺等,如何使得车间厂房等作业单位合理布置到总平面中,使工厂物流合理通畅呢?设施规划中的系统布置设计SLP可以帮助我们实现。
以下面的问题为例:
某玩具厂有8个车间M1~M8,两两之间的物料搬运量参见下表。
可提供的工厂如图所示,
布置设计就是合理的将M1~M8分配到A~H 8个工作地点,使得总物流量尽可能小。
假设上图的黑色线条为物流通道或路径,8个车间的面积相等。两车间之间的物料搬运不能穿越第三个车间,例如,有物料要从A车间搬到E车间去就不能穿越C车间,只能按这样的路线走:从A的中心出发垂直往下到达中间通道,经过C车间到达E/F车间的中间通道,再垂直向上运动到E的中心。相邻车间之间可以直接搬运,例如,有物料从F搬运到H,则可以直接从F车间的中心搬运到H的中心。以40m为一个标准移动单位,F→H的距离为1个标准移动单位,而A→E的距离为3个标准移动单位,以此类推,得到这8个工作地的距离从至表参见下表。
2.创建新项目
2.1在模型中添加对象
添加完成如上图所示。
2.2 添加全局变量
其中,1)Number_Of_Machine:作业单位数量,本例为8个;2)PartsNo:记录仿真模型在后续仿真过程中搬运的零件数,初始值为零;3)X_pos_init/Y_pos_init:定位M1~M8作业单位位置的初始坐标。Plant Simulation绘图坐标采用像素表达,坐标原点(0,0)位于模型层的左上角,水平往屏幕右方为x方向,垂直向屏幕下方为y方向。
2.3 从至表数据处理
点击W_from_to_Data对象,然后如下设置(点击黄色框设置格式):
以同样的方法,设置距离从至表,
2.4 从至表数据校核
右上角数据不能小于零;对角线数据必须等于零;左下角可以没有数据,如果有数据的可以不与右上角数据相等。例如,工作地1~3的距离为10,工作地3~1的距离为0或者10的话,表示物料在两工作地之间走的是同一条路线,如果工作地3~1的距离不是10而是其他数据,则表示来回搬运物料走的路线不一样,这样是允许的。所有与初始化有关的任务均在方法InitPartsTable中完成,双击InitPartsTable打开,输入如下SimTalk语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | is i, j, Rows, Lines: integer;--整数型局部变量 MachineName,BufName: string;--字符串型局部变量(机器、暂存区名称) Machine,Buf: object; --对象型局部变量 do if Number_Of_Machine/=D_From_To_Chart.YDim then inspect messageBox("设置数目不对!请假查...,",50,13) when 16 then print "Yes"; when 32 then print "N0"; else print "Cancel"; end; EventController.stop; -- 终止仿真 end; PartsTable.delete; -- 清除零件表的内容 for i:=1 to Number_Of_Machine loop --检查距离从至表的数据是否有错 for j:=1 to Number_Of_Machine loop if j<i then if D_From_To_Chart[j,i]<=0 then D_From_To_Chart[j,i]:=D_From_To_Chart[i,j]; end; else if j=i then D_From_To_Chart[j,i]:=0; -- 设施本身距离为0 else if D_From_To_Chart[j,i]<=0 then -- 设施之间距离小于等于0 messageBox("距离小于等于0..., ",50,13); EventController.stop; -- 终止仿真 end; end; end; next; next; end; |
运行之后的距离从至表:
3. 仿真建模思路
有了两个从至表及初始数据后,观察模型的构建过程如下:
1)表MachineSequence机器序列存放的8台机器(题目原意是8个车间,简单起见,一个车间看做8台机器)对应A~H的8个工作地,例如,序列M1,M2,…,M8和8个工作地对应关系参见下表第一行,M3,M1,M5,M8,M2,M7,M4,M6和8个工作地对应关系参见下表第2行。
2)一旦确定某设备定位在某工作地,仿真模型就必须在已确定的工作地指派相应的机床,用SingleProc物流对象来表示机床。
如果模型中原来有机器及其前面的缓冲区,先将这些对象删除,在方法InitPartsTable的最后添加如下SimTalk语句(注意必须加在最后一行程序end之前):
1 2 3 4 5 6 7 8 9 10 11 12 | for i:=1 to Number_Of_Machine loop -- 删除模型中原来的设施 MachineName := sprint("M",i); if existsObject(MachineName) then -- 如果模型中原来有机器 Machine := str_to_obj(MachineName); Machine.deleteObject; -- 删除模型中机器对象 end; BufName := sprint("BF",i); if existsObject(BufName) then -- 如果模型中原来有暂存区 Buf := str_to_obj(BufName); Buf.deleteObject; -- 删除模型中暂存区对象 end; next; |
4. 建立QAP模型
4.1 定义机器序列
4.2 定义零件加工顺序表
设置source,如下图
打开part,添加两个变量如图所示:
4.3 生成机器及其前置暂存区
打开InitPartsTable方法,在其最后添加如下SimTalk语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | Lines := 0; for i:=1 to Number_Of_Machine loop -- 从至表转换成仿真用的零件, 遍历机器序列表 Rows := str_to_num(Omit(MachineSequence[1,i],1,1)); --读取机器序列中的当前机器编号 MachineSequence[2,i] := Rows; for j:=1 to Number_Of_Machine loop -- 读取搬运量从至表的第Rows行 if W_From_To_Chart[j ,Rows]>0 then -- 如果机器Rows对其他机器有物料搬运 Lines := Lines + 1; PartsTable[1, Lines] := str_to_obj(sprint(".",location.name,".Parts")); -- 填写PartsTable零件表 PartsTable[2, Lines] := W_From_To_Chart[j ,Rows]; PartsTable[3, Lines] := sprint("Parts"); PartsTable[5, Lines] := Rows; -- Parts将流向的源机器 PartsTable[6, Lines] := j; -- Parts将流向的目的机器 end; next; MachineName := sprint("M",Rows); -- 生成机器 Machine:=.MaterialFlow.SingleProc.createObject(current,X_pos_init+D_From_To_Chart[Number_Of_Machine+1,i], Y_pos_init+D_From_To_Chart[Number_Of_Machine+2,i]); Machine.Name := MachineName; Machine.ProcTime := 5; Machine.label := sprint("机器_",Rows); Machine.ExitCtrl := ref(Leave); BufName := sprint("BF",Rows); -- 生成Buffer Buf:=.MaterialFlow.Buffer.createObject(current,X_pos_init+D_From_To_Chart[Number_Of_Machine+1,i]-35, Y_pos_init+D_From_To_Chart[Number_Of_Machine+2,i]); Buf.Name := BufName; Buf.Capacity := 5000; Buf.ProcTime := 0; .MaterialFlow.Connector.connect(Buf, Machine); -- 机器和暂存区连接起来 next; |
这时还不能运行EventController仿真,先在Init方法中添加下面SimTalk语句:
1 2 3 4 5 6 7 8 9 10 11 | is i: integer; do HandlingCost :=0; PartsNo:=0; InitPartsTable; -- 调用InitPartsTable初始化零件表 GASequence.delete; /*For i:=1 to Number_Of_Machine loop GASequence[1,i]:=i; next;*/ End; |
4.4 调入Load和Leave
load策略如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | is i,no,m : integer; Buf : object; do m:=0; no:[email protected]; for i:=1 to PartsTable.YDim loop if PartsNo=m and no<= PartsNo+PartsTable[2,i] then @._From:=PartsTable[5,i]; @._to:=PartsTable[6,i]; if no= PartsNo+PartsTable[2,i] then PartsNo:=PartsNo+PartsTable[2,i]; end; i:=PartsTable.YDim+1; -- 跳出循环 end; m:=m+PartsTable[2,i]; next; -- 先将零件送到From位置 Buf := str_to_obj(sprint("BF", @._From)); @.move(Buf); end; |
然后将load加入source,
Leave的策略如下:
5. 布置设计的优化
5.1 设置GA
打开编辑,插入以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | :boolean is i:integer; chrom: table;obj:object; do chrom := individual[1,1]; result := true; obj := .LayoutDesign.QAP.MachineSequence; obj.sort(2,"up"); for i:=1 to obj.YDim loop obj[3, chrom[1,i]] := i; next; obj.sort(3,"up"); end;-- of the method |
点击按表,打开:
5.2 仿真结果
6. 思考
该方法达到最优解了吗?