Flexsim中的重要概念及开发技术.pdf
先天下之忧而忧,后天下之乐而乐。范仲淹以铜为镜,可以正衣冠;以古为镜,可以知兴替;以人为镜,可以明得失。旧唐书魏征列传 第五章 Flexsim 相关的概念及关键技术研究 Flexsim 软件介绍 Flexsim 是由美国的 Flexsim Software Production 公司出品的,是一款商业化离散事件系统仿真软件。Flexsim 采用面向对象技术,并具有三维显示功能。建模快捷方便和显示能力强大是该软件的重要特点。该软件体供了原始数据拟合、输入建模、图形化的模型构建、虚拟现实显示、运行模型进行仿真试验、对结果进行优化、生成 3D 动画影像文件等功能,也提供了与其他工具软件的接口。图 5-1 是 Flexsim 软件及其构成模块的结构图7。图 5-1 Flexsim 功能结构图 Flexsim 提供了仿真模型与 ExpertFit 和 Excel 的接口,用户可以同过 ExperFit 对输入数据进行分布拟合,同时可以在 Excel 中方面地实现和仿真模型之间的数据交换,包括输出和运行模型过程中动态修改运行参数等。另外该软件还提供了优化模块 Optquest,增加了帮助迅速建模的 Microsoft Visio 的接口。Flexsim 软件的主要特点 Flexsim 仿真软件的特点主要体现在采用面向对象技术,突出 3D 显示效果,建模和调试简单开放方便,模型的扩展性强,易于和其他软件配合使用等方面。(1)基于面向对象技术建模 Flexsim 中所有用来建立模型的资源都是对象,包括模型、表格、记录、GUI 等。同时,用户可以根据自己行业和领域特点,扩展对象,构建自己的对象库。面向对象的建模技术使得 Flexsim 的建模过程生产线化,对象可以重复利用,从而减少了建模人员的重复劳动。(2)突出的 3D 图形显示功能 Flexsim 支持 OpenGL 技术,也支持 3ds、wrl、dxf 和 stl 等文件格式。因此用户可以ExpertFit 等拟和分布工Excel等可以用作统计分析的工具 模型建立与调试 模型有效性确认 运行仿真试验 3D的可视化 结果动态显示 生成影像文件 Flexsim仿真软件 Microsoft Visual 输入建模 系统仿真 运行仿真试验 结果统计分析 老当益壮,宁移白首之心;穷且益坚,不坠青云之志。唐王勃老当益壮,宁移白首之心;穷且益坚,不坠青云之志。唐王勃建立逼真的模型,从而可以帮助用户对模型有一个直观的认识,并帮助模型的验证。用户可以在仿真环境下很容易地操控 3D 模型,从不同角度、放大或缩小来观测。(3)建模和调试的方便 建模过程中用户只需要从模型库中拖入已有的模型,根据模型的逻辑关系进行连接,然后设定不同对象的属性。建模的工作简单快捷,不需要编写程序。(4)建模的扩展性强 Flexsim 支持建立用户定制对象,融合了 C+编程。用户完全可以将其当作一个 C+的开发平台来开发一定的仿真应用程序。(5)开放性好 提供了与外部软件的接口,可以通过 ODBC 与外部数据库相连,通过 socket 接口与外部硬件设备相连,与 Excel、Visio 等软件配合使用。Flexsim 的一些重要概念 Flexsim 是目前国内最新的仿真软件,关于该软件的资料和使用经验还很少。作者是在不断的摸索中学习的,所以希望本文能对其他人有一定的借鉴。要完全掌握好 Flexsim,并将其用到我们的工作、学习和研究当中,理解该软件的一些重要概念和思想是很重要的,本节针对集装箱码头建模仿真中用到的技术做一个梳理。面向对象的思想 相对于目前的一些仿真软件(如 Witness,eM-Plant 等),Flexsim 是采用面向对象思想和技术开发的,其本身更是用 C+语言实现。严格地说该仿真软件包括了两部分,仿真软件和后台支持环境 VC+.NET。由于 C+是一种面向对象的语言,所以使用 Flexsim 软件,从用户用于系统建模,或是做一些二次开发,这些工作都有面向对象思想的体现。可以这样说,没有领会面向对象的思想,就不能完全发挥 Flexsim 软件本身的特点,也就不能用其实现用户的目的。使用 Flexsim 软件的用户需要对 C+语言有一定程度的熟悉。本节主要是解释Flexsim 中所特有的一些面向对象思想,而不涉及面向对象语言的解释(关于 C+语言的知识请查看相关书籍)。对象(Object)的概念在 Flexsim 软件中无处不在,我们先直观的感受一下。软件的运行界面左边是一个常用的对象库(如图 5-1)。库中的各种部件就是有特定功能的对象,这些对象是软件本身自带的,使用这些基本的部件对象用户可以完成大多数的仿真工作。我们使用 Processor 来解释一下对象的概念:我们日常所见的任何具体事物都可看作是对象,这里 Processor 就是一种设备,它的作用就是对经过他的物件进行一些加工,即改变物件的状态。这里我们可以将其当作现实中的设备,如机床等。老当益壮,宁移白首之心;穷且益坚,不坠青云之志。唐王勃古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。苏轼 图 5-1 这里我们借用 C+程序设计语言中的对象的概念。对象是类的实例,类是对现实对象的抽象。类中包含了对象的数据(相当于现实对象的状态),以及对象的方法(相当于现实对象用来处理外界所给信息的方法)。对象封装了属性和方法,进一步到 Flexsim 中,对于软件中可用的库对象,他们本身有自己的属性(如颜色,尺寸,位置等),还有处理物件的方法。在使用软件的过程中,我们完全可以以人们平时的思维方式来思考,而无须过多的抽象化,这也就是面向对象方法的优点。Flexsim 的对象层次结构 面向对象方法的一个优点是类与类之间可以有继承关系,对象的继承性给我们提供了更大的柔性来扩展我们自己的对象,即衍生出新的对象。在 Flexsim 中我们可以充分利用继承性来开发我们自己的对象,而软件本身也给用户提供了这样的机制。Flexsim 本身的库对象是高度抽象化的,具有很强的通用性,几乎涵盖了仿真中可能遇到的所有对象。这些对象之间有一定的继承关系,他们之间存在着逻辑关系。下图(图 5-2)是 Flexsim 中对象的层次结构。谋事在人,成事在天!增广贤文其身正,不令而行;其身不正,虽令不从。论语 从类的派生关系图中我们可以对 Flexsim 中各种对象的逻辑关系一目了然。对象库中的对象分为两种,一种是从 FixedResource 中派生下来的,另一种是 TaskExecuter 中派生下来的。通过分析我们不难发现,从 FixedResource 中派生来的对象有一个共同的特点,其本身是不会运动的,他们的作用只是产生或消除物件、存储物件、加工物件等等;从TaskExecuter 中派生的对象,其本身是可以运动的,其作用是将物件从一个地点运送到另一个地点。当现有的库对象不能满足用户的需要时,用户就需要创建自己的对象。Flexsim 为用户提供了这样一种机制用户可以定制自己的库对象。在对象层次图中,我们看到有两个虚线框,这表示用户可以从 FixedResource 和 TaskExecuter 中派生出自己的对象。Flexsim的早期版本中从这两个类中派生新的对象比较复杂,最新的版中增加了 BasicFR 和 BasicTE类,使用户的开发工作更容易。后面的章节中将具体介绍怎样来实现一个新对象的定制。节点和树 在介绍树结构之前,我们先来了解 Flexsim 中节点(node)的概念。节点是树结构的最基本的组成单元,他们组成了链接的层次。所有的节点都有一个文本FlexsimObjecFixedResourcDispatchNavigatoNetworkNode Source Queue Sink Conveyor Rack ReservoiFixedSourceTemplate ProcessoTaskExecuter Combiner SeparatoOperator TranspotCrane ASRSvehicle NetworkNavigatoCraneNavigatoBasicTE BasicRF 天行健,君子以自强不息。地势坤,君子以厚德载物。易经古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。苏轼缓冲区,用来保存节点的名字。节点可以是其他节点的容器,可以是用来定义一个对象属性的关键字,或是拥有一个数据项。属于一个节点的数据项类型可能是:数值(number),字符串(string),对象(object),或指针(pointer)。下面列出 FLexsim 中不同类型的节点标志:标准(Standard):对象(Object):属性/变量(Attribute/Varibale):函数(Function(C+)):函数(Function(FlexScript)):用户可以在对象的树结构中任意地操作节点,例如增加节点,删除节点,改变节点所包含的值等。含有对象数据(Object)的节点可能包含有节点的子列表。含有对象数据的节点称之为对象节点。当你单击一个对象节点时,你会看到在节点的左边有一个大于号()。单击将打开对象数据的树分支。如果一个节点包含子节点,可以按下+按钮来展开。如果一个节点包含对象数据,可以按下来展开。下图(图 5-3)展示了一个队列(Queue)展开的对象数据树。图 5-3 树结构(tree)是一种很常用的数据结构。Flexsim 仿真模型中的对象,或对象中的属性和方法节点等都是树结构;用户甚至可以直接在树结构中操作对象。在 Flexsim 中有两个主要的对象类型:模型(Model)或仿真对象(Simulation Object)和视图对象(View Object)。两种类型都有对象数据树,包含了属性和行为控件。一个对象节点的对象数据树中的节点可以作为属性、变量或成员函数。也有只是作为简单的容器来包含节点以达到组织的目的。任务序列 任务序列(Task Sequences)是 Flexsim 仿真软件中的核心机制。各种复杂仿真的实现很大程度取决于怎样实现任务序列。前面介绍了 Flexsim 中有两种对象,一种是派生至FixedResource 的静态对象(即对象本身不运动);另一种是派生至 TaskExecuter 的动态对吾日三省乎吾身。为人谋而不忠乎?与朋友交而不信乎?传不习乎?论语天行健,君子以自强不息。地势坤,君子以厚德载物。易经象(即对象本身可运动)。如果用户建立的系统模型全部使用了静态对象,那么就不需要任务序列的机制,但是这种情况几乎没有。使用动态对象搬运物件,对象怎样运动,实现什么样的功能等,这就需要。任务序列是由 TaskExecuter 执行的一组命令序列。这里 TaskExecuter 涵盖了所有派生自他的动态对象,如 Operators,Transpoters,Crane,ASRSvehicle,Robots,Elevators以及其他可运动的对象。图 1-4 表示一个任务序列,该任务序列有多个任务组成。图 1-4 Flexsim 中为用户提供了功能齐备的任务类型。常用的任务序列有:TASKTYPE_TRAVEL、TASKTYPE_LOAD、TASKTYPE_UNLOAD、TASKTYPE_TRAVELTOLOC 等。不同的任务序列有不同的设置参数,用户可以根据需要在使用的时候查询帮助文档。默认任务序列 FixedResource 为了将物件(item)移至下一个站点(station),有一个创建任务序列的默认机制。FixedResource 对象的参数对话框中一个通用的“Flow”选项页,选择其中的“Use Transport”复选框,这样就可创建默认的任务序列。对于 Processor 对象,还可以自动创建对 Setup time/Process time/Repair operation 的任务序列。当仿真运行时,这些自动创建的任务序列就会传递给与其中心端口相连的动态对象来执行。这里给个简单的例子说明。假设用户选择了 Queue 对象参数对话框的“Flow”选项页中的“Use Transport”复选框,当系统运行时,产生了如下任务序列:当 Operator 收到该任务序列时,顺序地执行任务序列中的每个任务,执行过程如下:Operator 先移动到 Queue 处(Travel);接着拿起物件(Load);然后移动到下一个站点处(Travel);最后放下物件(Unload)。在仿真运行的任意时刻,一个 TaskExecuter 只能执行一个任务序列,而此时FixedResource 可能创建了许多任务序列,这些未执行的任务序列被放置在缓存队列中等待执行。定制任务序列 一般情况下,默认的任务序列就可以满足仿真要求。有时候用户需要为某些特定的工艺、多个设备的组合操控灯定制任务序列。这里分三种介绍定制任务序列,第一种是创建最简单的、只分配给一个对象执行的任务序列;第二种是由多个对象协同作业的任务序列。定制简单任务序列 P1 P2 Travel Load Break Travel Unload P1 P2 Task1 Task2 Task3 Task4 Simulation Time P1:Priority Value P2:Preempt Value 穷则独善其身,达则兼善天下。孟子吾日三省乎吾身。为人谋而不忠乎?与朋友交而不信乎?传不习乎?论语使用 3 条命令来创立任务序列,命令执行的顺序如下:createemptytasksequence();inserttask();dispatchtasksequence();从函数名就可以看出创建任务序列的过程。首先创立一个空的任务序列,然后在此任务序列中插入具体的任务,最后发布该任务序列。我们举个简单的例子,叉车运动到集装箱旁边,然后装载集装箱。在这个过程中,涉及了两个任务:运动(TASKTYPE_TRAVEL)和装载(TASKTYPE_LOAD)。具体实现如下:fsnode*new_ts=createemptytasksequence(forklift,0,0);inserttask(new_ts,TASKTYPE_TRAVEL,station);inserttask(new_ts,TASKTYPE_LOAD,item,station,2);dispatchtasksequence(new_ts);这里叉车(forklift)是任务序列的执行者,我们为其创建了一个新任务序列(new_ts),在此任务序列中插入具体的任务(TRAVEL/LOAD),最后发布任务序列。我们在创建新任务序列时,createemptytaskseqence 函数的第一个参数 forklift 可以是该任务序列的执行者,或者是 Dispatcher 对象。关于 Dispatcher 对象的作用下一小节有具体的介绍。后两者参数决定了该任务序列的优先级别,我们可以根据任务的紧急程度来定义任务序列的执行顺序。Inserttask 函数插入具体的任务类型。第一个参数表示该任务所属的任务序列。前面提过不同的任务类型有着不同的代码,以及不同的参数选择。这些参数分别是:Task Type/involved1/involved2/var1/var2/var3,有些参数是选择性的,这要根据任务类型来决定。这里以 TASKTYPE_LOAD 为例,图 1-5 表示了不同参数的意义。用户可以根据所示规则查询具体的任务的参数选项。图 1-5 任务 Load 的参数含义 协同作业的任务序列 P1 P2 TRAVEL LOAD Task Type:LOAD Involoved1:bject to load?item Involoved2:object to load from?Var1:output port?2 Var2:0(默认值)Var3:0(默认值)其身正,不令而行;其身不正,虽令不从。论语吾日三省乎吾身。为人谋而不忠乎?与朋友交而不信乎?传不习乎?论语协同作业的情况有很多,比如叉车作业需要一个司机来操控,或者一件物品需要两个人来同时搬运等。在 Flexsim 中叉车、人都是可运动对象,要实现协同作业的任务序列相对于只对一个对象创建任务序列要复杂许多。我们以叉车和司机的协同工作为例来说明怎样实现协同作业的任务序列。我们先来分解任务的执行过程:1)人运动到叉车上(Travel);2)人进入驾驶室(这里是叉车的动作)(Load);3)叉车运动到指定地点(Travel);4)叉车装载货物(Load);5)叉车运动到卸载点(Travel);6)卸载货物(Unload)。图 1-6 是叉车和人的任务序列。图 1-6 协同任务序列 从图中可以看出,叉车在人到达之后才执行任务,人进入叉车之后就随着叉车一起完成叉车的任务。人的任务序列中只有一个任务,其他时间不做任何事情。在 Flexsim 中实现的代码要复杂一些,调用的函数与前面所讲的函数不同。涉及的函数主要有:createcoordinatedtasksequence();insertallocatetask();insertproxytask();insertsynctask();insertdeallocatetask();dispatchcoordinatedtasksequence();一个协同作业的任务序列的定制是很复杂,也是很容易出错的。在开始实现之前必须分析清楚作业的过程。对于前面人操控叉车的例子我们已经将作业流程分析清楚了,下面是具体的实现,我将每个函数的功能写在程序的注释当中。/创建协同任务序列 fsnode*myts=createcoordinatedtasksequence(operatorteam);/为每个执行对象分配任务 int opkey=insertallocatetask(myts,operatorteam,0,0);int forkliftkey=insertallocatetask(myts,forkliftteam,0,0);/人的分派任务序列 int traveltask=insertproxytask(myts,opkey,?TASKTYPE_TRAVEL,forkliftkey,NULL);insertsynctask(myts,traveltask);/叉车的分派任务序列 insertproxytask(myts,forkliftkey,TASKTYPE_MOVEOBJECT,opkey,forkliftkey);insertproxytask(myts,forkliftkey,TASKTYPE_TRAVEL,loadstation,NULL);insertproxytask(myts,forkliftkey,TASKTYPE_LOAD,item,loadstation);insertproxytask(myts,forkliftkey,TASKTYPE_TRAVEL,unloadstation,NULL);P P Wait Load Travel Load Travel P P Travel to forklift Wait ForklifOperato丈夫志四方,有事先悬弧,焉能钧三江,终年守菰蒲。顾炎武我尽一杯,与君发三愿:一愿世清平,二愿身强健,三愿临老头,数与君相见。白居易大丈夫处世,不能立功建业,几与草木同腐乎?罗贯中百学须先立志。朱熹运输由集卡来实现,这样集卡就有这样一个任务序列:Travel?Load?Travel?Unload。集装箱装入集卡的作业由岸桥设备完成,卸载放入堆场的作业由场桥设备完成,所以集卡的任务序列中 Load/Unload 的任务就应该由岸桥和场桥来完成。岸桥完成一次作业的过程也就是完成一个任务序列的过程,可以知道岸桥完成的任务序列应该是:Travel?Load?Travel?Unload。岸桥在作业的过程中,集卡处于等待的状态,也就是说岸桥和集卡之间是协同作业的。场桥的情况与岸桥一致。Flexsim 中可以使用调用子任务的方法将岸桥和场桥的任务序列插入到集卡的任务序列中。图 1-7 表示了主任务序列和子任务序列之间的关系。图 1-7 集装箱搬运过程的任务序列 在 Flexsim 中的实现的主要代码如下,其关键的代码在文中有注释:/获取任务序列中的任务数量 int nroftasks=gettotalnroftasks(tasksequence);/查找 Load/Unload 任务,找到之后调用子任务来替换这两个任务 for(int i=1;i=nroftasks;i+)int tasktype=gettasktype(tasksequence,i);switch(tasktype)case TASKTYPE_LOAD:case TASKTYPE_FRLOAD:int msgtype=(tasktype=TASKTYPE_LOAD 1:2);Travel Load Travel Unload 场桥子任务序列 Travel Load Travel Unload 集卡主任务序列 Travel Load Travel Unload 岸桥子任务序列 谋事在人,成事在天!增广贤文天行健,君子以自强不息。地势坤,君子以厚德载物。易经/changetask()函数会发出一个消息(message),我们在消息的接受者的 OnMessage()函/数中创建岸桥和叉车的子任务序列 changetask(tasksequence,i,TASKTYPE_CALLSUBTASKS,current,NULL,msgtype,tonum(gettaskinvolved(tasksequence,i,1),tonum(gettaskinvolved(tasksequence,i,2),gettaskvariable(tasksequence,i,1);break;case TASKTYPE_UNLOAD:case TASKTYPE_FRUNLOAD:int msgtype=(tasktype=TASKTYPE_UNLOAD 3:4);changetask(tasksequence,i,TASKTYPE_CALLSUBTASKS,current,NULL,msgtype,tonum(gettaskinvolved(tasksequence,i,1),tonum(gettaskinvolved(tasksequence,i,2),gettaskvariable(tasksequence,i,1);break;default:break;子任务序列的实现过程,关键代码有注释/由 changetask()传过来的参数 msgtype int msgtype=msgparam(1);switch(msgtype)/这里只实现了岸桥子任务序列 case 1:case 2:fsnode*op=msgsendingobject;fsnode*item=tonode(msgparam(2);fsnode*queue=up(item);/queue1 fsnode*active_ts=gettasksequence(op,0);/0 表示当前活动的任务序列 int port=gettaskvariable(active_ts,getcurtask(active_ts),4);fsnode*sts=centerobject(queue,2);/注意连接时候的端口号 fsnode*ts=createcoordinatedtasksequence(op);/创建协同工作序列 int op_key=insertallocatetask(ts,op,0,0);int sync_key1=insertproxytask(ts,op_key,TASKTYPE_PICKOFFSET,item,queue,0,1,0);int sts_key=insertallocatetask(ts,sts,tonum(queue),0);insertproxytask(ts,sts_key,TASKTYPE_TRAVEL,queue,NULL,0);int sync_key2=insertproxytask(ts,sts_key,msgtype=1 TASKTYPE_FRLOAD:TASKTYPE_LOAD,item,queue,port);insertsynctask(ts,sync_key1);老当益壮,宁移白首之心;穷且益坚,不坠青云之志。唐王勃先天下之忧而忧,后天下之乐而乐。范仲淹 insertsynctask(ts,sync_key2);int sync_key3=insertproxytask(ts,sts_key,TASKTYPE_UNLOAD,item,op,port);insertsynctask(ts,sync_key3);insertdeallocatetask(ts,op_key);insertdeallocatetask(ts,sts_key);return tonum(ts);break;具体的代码实现部分有些复杂,但是我们通过前面的分析,将思路整理清楚了,实现也就相对容易了。运动学(Kinematics)运动学部分是版中新引入的功能。Flexsim 软件的一大特点就是三维显示功能非常强大,除了能够处理数据统计外,还能使模型的场景中的可运动设备动起来,从而使模拟过程更接近真实。例如,对于港口设备岸边集装箱岸桥,在系统建模中如果我们只关心数据、处理时间等的话,可以用 Processor 来简单代替岸桥的功能。在 Flexsim 中可以将岸桥完全实现,不仅在外观上,更重要的是设备处理物件的动作。要实现设备的动作,平移(水平运动,或垂直运动),或是旋转运动,这些都需要用到运动学的知识。本节主要介绍运动学相关知识,例如坐标空间转换,运动实现,模型的导入,尺寸大小的设定等,还将详细讲述如何在 Flexsim 中创建用户自己的专门对象库。FLexsim 中的坐标空间 运动功能允许一个对象同时实现多个移动操作,在每个运动方向都有加速度、减速度、起始速度、结束速度以及最大速度等属性。例如集装箱岸桥的运动就是由多个部件的运动组成,即大车沿着轨道运行,小车在大车上运行,吊具在垂直方向上运行。大车、小车和吊具都有其自己的速度属性。如果有了岸桥这样一个设备对象,在仿真过程中我们就可以实现岸桥的作业过程。运动学函数的引入就是帮助用户来实现自己定制的设备的动作。运动学这部分是从版才开始引入的,新版本还会对这部分进一步改进。实现运动学并不难,主要是对三个函数的使用。要执行运动操作,首先要调用initkinematics 命令。该命令为运动初始化数据,保存对象的起始位置、起始角度。初始化完毕之后,调用 addkinematic 命令为对象添加平移或旋转动作。例如,用户告诉对象在5 秒钟时开始运动,给定加速度、减速度和最大速度,在 x 方向上平移 10 个单位;然后告诉对象在 7 秒钟时,用不同的加速度、减速度和最大速度,在 y 方向上移动 10 个单位。这两个运动的结果是:对象先在 x 方向上运动,然后同时在 y 方向上加速,最后到达目的点的运动轨迹是抛物线。每一个单独的运动通过 addkinematic 命令添加;然后调用updatekinematics 命令在运动过程中不断地刷新视图,该命令的作用就是计算对象当前的位置和旋转角度。吾日三省乎吾身。为人谋而不忠乎?与朋友交而不信乎?传不习乎?论语勿以恶小而为之,勿以善小而不为。刘备上面的例子很简单,为了更好的解释运动学,我们先介绍坐标空间的概念。Flexsim 中最常用的坐标空间就是模型空间(model)。用户建立系统模型时,将许多对象放入视图中,根据不同的逻辑关系组成不同的模型。这些对象都处于模型空间中。模型空间是 Flexsim中最大的坐标空间,系统模型中的所有对象都被包含在这个空间当中。还有一种容器坐标空间(container)。容器对象就是可以存储物件,举例来说,对象Queue 的作用是暂存物件,此时 Queue 就相当于一个容器。当物件置于 Queue 中时,物件(item)就处于 Queue 的容器空间中。当一个物体处于不同的容器空间中时,他的位置坐标就是他所在容器坐标空间坐标系的值。图 1-5 描述了容器空间的概念。图 1-5 在上图中,Queue 置于 Model 坐标空间中,其位置坐标是(2,3);物件 item 置于 Queue的容器空间中,其位置坐标是(1,-2)。用户查看对象的属性页面可以得到对象的坐标值。这里需要提示一点的是,在 Flexsim 中每个被选中的对象都有一个黄色的外界矩形框,对象的坐标是以如图所示的位置点来确定的。当用户定制的对象需要做的动作比较复杂的动作时,相对运动的坐标关系常常需要在不同的容器坐标系之间作相应的转换工作。Flexsim 也提供了相应的坐标转换函数,在我们后面的内容中会有详细的讲述。模型的导入及模型尺寸的调整 Flexsim 可以导入多种 3D 媒体文件。这些文件格式包含了 3ds、wrl、dxf 和 stl,这些都是很常用的工业标准。用户可以使用第三方的软件,如 3DMax,MAYA 来构造模型,然后将模型文件转化为标准格式,最后导入 Flexsim 中。这里有一些注意点,Flexsim 只支持版的图形,不支持版本;Flexsim 只能导入 stl 的 ascii 文件,不支持 stl 的二进制(binary)文件。在对象的属性对话框中,在“3D shape”一项中可以更改 3D 模型。我们这里用一个实例来介绍一个制作的过程,以及一些技巧。3 2 物件 item OO Z X Y XYZModel 空间 Queue 空间-2 1 常将有日思无日,莫待无时思有时。增广贤文勿以恶小而为之,勿以善小而不为。刘备制作一个集装箱吊具。我们在 3DMax 中按比例做出吊具的三维模型,具体的制作过程不是我们讨论的内容。在完成之后我们还需要做一些后期的处理工作,使导出的文件在导入Flexsim 中有最佳的效果。实现过程很简单,过程如下:量取模型长宽高的比例值(:);设置 3DMax 软件的最小单元的尺寸为 1;将已完成的模型缩小到尺寸大小为 1 的正方体中(图1-6);最后将模型导出,保存为 3ds 文件格式。图 1-6 我们选取 BasicTE 作为吊具的模板。在 Flexsim 中,BasicTE 对象的默认外形是一个球体。打开该对象的属性对话框,将“3D shape”选项中的媒体文件改为吊具模型的文件。导入之后的外观如图 1-7 所示。在“Position,Rotation,and Size”选项中,将 SX/SY/SZ三项的值改为我们之前记录的外界矩形框的长宽高比例,效果如图 1-8。图 1-7 图 1-8 从图中我们看到,不管模型的尺寸怎么改变,模型始终被包含在黄色的外界框中。这么做的目的不仅是为了外观上的好看,更重要的是为了后面的运动学计算时的方便。有时候模型导入 Flexsim 后,模型和外界框之间有一些偏差。此时可以使用“Edit 3D Shape Factors”来修正,使模型完全处于黄色的外界框之中。除了改变模型的外观和尺寸,Flexsim 中还可以改变对象外观的贴图和颜色。还有一点就是用户还可以自己编写 OpenGL 代码来画出对象的外观,具体实现在“Custom Draw Code”中实现。这部分的要求比较高,用的不是很多。百学须先立志。朱熹大丈夫处世,不能立功建业,几与草木同腐乎?罗贯中制作岸边集装箱起重机对象 岸桥设备是集装箱码头装卸船作业的最主要设备,在港口中有着广泛的应用。我们用一个例子来总结一下本节所讲的内容,该例子对于其他对象的开发有很多的借鉴意义。岸桥设备对象在后面的系统建模及仿真中也得到了应用。BasicTE 对象 我们前面提到,Flexsim 为用户提供了 BasicTE/BasicFR 对象来开发自己的对象。岸桥设备的实现要涉及到运动学,所以我们选择 BasicTE 对象。BasicTE 对象的引入就是为了实现 对 象 的 运 动,我 们 主 要 的 工 作 就 要 实 现 参 数 对 话 框 中OnBeginOffset/OnUpdateOffset/OnFinishOffset 函数。图 1-9 BasicTE 参数对话框 这些函数的功能是:(1)OnBeginOffset 计算出该对象在 X/Y/Z 方向上要运行的距离或要旋转的角度;(2)OnUpdateOffset 在对象运动的过程不断地计算位置或旋转的角度;(3)OnFinishOffset 运动结束后调用该函数。部件的层级关系 这里我们将岸桥分解为三个主要运动部件:大车运行结构、小车运行机构和吊具。三者先天下之忧而忧,后天下之乐而乐。范仲淹以家为家,以乡为乡,以国为国,以天下为天下。管子牧民之间的关系是:小车和吊具跟随大车运动;吊具跟随小车运动。也就是说大车和小车都相当于容器,小车置于大车容器中,吊具置于小车容器中。在 Flexsim 中的实现如图 1-10 图 1-10 岸桥部件层次关系 导入三维模型 我们在 3DMax 中制作三维模型,调整三个部件的比例关系,记录模型外接矩形框的窗宽高比例,然后将三个部件分别导出,保存为:。将三个模型导入到各自对应的 BasicTE 对象。调整尺寸之后得到的如图 1-11 所示的效果。图 1-11 三维模型导入 Flexism 后的效果 运动的实现 我们先来看看 BasicTE 是怎样来完成一次搬运的作业过程的。图1-12 表示的是 BasicTE与物件容器(容器 1 是提取物件的位置,容器 2 是物件放下的位置)的相对位置。BasicTE先运动到容器 1;提取物件;在运动到容器 2;放下物件。这就是一个完整的作业过程。我们关心的是 BasicTE 两次运动的行走路线,从图中我们可以清楚地看到有两种路线:1)在X/Y 方向上同时有运动速度,此时从起始点到目的点的运动路线是抛物线;2)将 X/Y 方向上的运动分解,可以先完成 X 方向上的运行距离,再完成 Y 方向上的运行距离,或反过来。大车运行机构 小车运行机构 吊具 BasicTE BasicTE BasicTE 忍一句,息一怒,饶一着,退一步。增广贤文勿以恶小而为之,勿以善小而不为。刘备 图 1-12 两种运行路线 确定了运行路线后,接下来需要知道的是 BasicTE 对象需要运动的距离。不管是抛物线的运行路线还是直线的运动路线,都可以将分解为起始点与目的点之间在 X 方向上的距离和Y 方向的距离。BasicTE 的参数对话框的“OnBeginOffset”选项中为我们提供了多个有用的参数值,这是系统自动计算的,用户只要明白这些参数的含义直接拿来使用即可。下面是对这些参数含义的解释:1)current:BasicTE 对象本身;2)x:BasicTE 在 x 方向上的偏移量;其值为 BasicTE 与物件中心坐标在 x 方向的差;3)y:BasicTE 在 y 方向上的偏移量;其值为 BasicTE 与物件中心坐标在 y 方向的差;4)z:BasicTE 在 z 方向上的偏移量;其值为 BasicTE 与物件中心坐标在 z 方向的差;5)item:BasicTE 所搬运的物件;6)endspeed:BasicTE 到达目的点的速度;7)maxspeed:BasicTE 运动过程中所能达到的最大速度;8)acceleration:BasicTE 运动过程中的加速度;9)deceleration:BasicTE 运动过程中的减速度;我们接着来看看我们做要完成的岸桥设备该怎样运动可以肯定岸桥不会有抛物线的运动轨迹,假设岸桥的大车(STS)在 model 坐标空间中沿着 y 方向运动,那么小车(Trolley)在大车的坐标空间中沿着 x 方向运动,吊具(Grabber)在 z 方向上运动。小车和吊具都处于大车的坐标空间中,当我们在大车中实现运动函数时,上面所提到的距离参数需要做一些转换。这里 x/y/z 距离参数都是大车的