Plant Simulation 学习笔记(一)
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语句:
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之前):
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语句:
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语句:
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策略如下:
is
i,no,m : integer;
Buf : object;
do
m:=0;
no:=@.getNo;
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
打开编辑,插入以下代码:
: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. 思考
该方法达到最优解了吗?