数控指令翻译及驱动
数控指令用于驱动机械执行特定的动作或设置某种状态。虽然每种机床的数控指令都有所不同,不同种类的机床的数控指令集差别很大,但可以根据指令分类为:驱动指令、状态指令。
驱动指令驱动机械进行一些特定的动作,例如常见的数控铣床的G01代表直线插补指令,用于驱动一个或几个轴进行直线插补动作。状态指令设置当前模式,例如常见的G41、G42命令设置当前的刀具插补方式。
在驱动虚拟机床时,必须要将数控指令进行翻译。传统的指令翻译方法将指令翻译为一个命令ID的链表,然后根据ID的不同进行指令区分。在本节中将运用设计模式来更好的解决指令翻译中的软件设计问题。
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。常见的设计模式有23种[15]。根据数控指令的特点,可以应用软件设计模式中的命令设计模式进行动作驱动,有限状态机的方式进行状态的存储和跳转。
命令设计模式
图 3-3使用UML图描述了整个命令设计模式。其中:
-
Command接口声明了执行命令的接口。
-
ConcreteCommand实现了Command接口,具体定义了命令的执行方法。其中Execute()函数实现真正的命令执行,他调用Receiver(接受者)的响应操作来实现。
-
Client通常是应用程序,它创建一个具体的命令对象并设置它的接受者。
-
Invoker对应于一个触发者,这个触发者可能是特定的按钮或者特定的条件,它要求命令执行这个请求。
-
Receiver作为接受者,知道如何实施与执行一个请求相关的操作。

图 3-3 命令设计模式UML图
在仿真平台中,每一种G代码都对应于一个ConcreteCommand类。对G代码进行分解后可得到一系列的ConcreteCommand类的实例。例如以下代码:
G01X0087Y2234Z234
G02X2345Y86234I2312
G42
G01X-987234Y09723Z234
分析以上代码可以得到图 3-4的结构。类图中有G01、G02、G42四个类,其功能相似,都继承于AbstractCommand,每个类单独的Execute()函数执行各自相关的动作。CommandSequence类较为特殊,它存储一个链表,此链表存储各种命令的实例的指针,可以模拟一批命令进行不可分割的顺序执行的过程。

图 3-4 命令及命令链表
通常,将这些命令对应的类的实例指针按照执行的顺序放入链表中,主程序并不需要直接知道这是什么指令,就能进行相关的动作。例如以上的命令可以有如下伪代码:
List<AbstractCommand> cmdList;
cmdList.Add(new G01_Command);
cmdList.Add(new G02_Command);
cmdList.Add(new G42_Command);
cmdList.Add(new G01_Command);
foreach cmd in cmdList
{
int rst=cmd->Execute(); //通过AbstractCommand接口进行统一调用。
//检查返回值并执行另外的动作
}
如果采用传统的应用链表存储指令ID的方式有以下的伪代码:
List<int> cmdList;
cmdList.Add(G01_ID);
cmdList.Add(G02_ID);
cmdList.Add(G42_ID);
cmdList.Add(G01_ID);
foreach cmd in cmdList{
switch(cmd){
case G01_ID:
do_G01_Command();break;
case G02_ID:
do_G02_Command();break;
case …
}
}
传统的思路和命令模式会的对比如表3-1。可见应用命令模式会减少设计的难度并且减少软件的维护成本。
表3-1 传统思路和命令模式对比
传统思路
命令模式
接口设计
无统一接口,较为混乱
统一的接口,通过AbstractCommand定义
添加指令的灵活度
增加ID和switch/case,增加指令会改变调用命令的逻辑。
增加相关类,调用逻辑不变。
高级应用
较难实现,并且维护起来较为麻烦。
可以通过操作链表来实现命令的增添、删除、撤销(Undo)操作等。
-
-
有限状态机
-
Context定义了用户感兴趣的接口并维护了一个ConcreteState子类的实例,定义了当前的状态。
-
State接口定义一个接口封装特性状态相关的行为。
-
ConcreteState实现了Context的一个状态相关行为。
在应用中,具体的State状态实例会被ConcreateState代替,Handle()函数的调用也会根据ConcreateState的不同而调用不同的Handle()。
图 3-5所示状态机可用图 3-7的UML图解释表示。应用程序初始化时Application类初始化了四个状态,并将初始化状态设置为Init状态(1)。此时State实质是Init类的实例。Init类拥有五个继承的函数。执行LoadFile()动作会执行loadFile()函数,并且将this指针指向新的FileLoaded类的实例。此时State指向了FileLoaded类的实例。执行其他四个函数并不会执行有效的过程,内存中State依然指向Init。
在FileLoaded状态下,执行Run()动作便会激发Run()函数,并且将当前this指针指向新的Running类的实例,这就实现了从FileLoaded到Running状态的转换。以此类推,Running类执行Stop()函数后将会将this指针指向FileLoaded类的实例。实现了从Running到FileLoaded的转换。

图 3-7状态转化
状态机编译器
在具体的实践中,状态机的代码量随着状态的增加而变得几乎不可能用人工完成,必须要有适当的工具辅助人工进行代码的编写。State Machine Compiler(见:http://smc.sourceforge.net/)就是这样一种工具,它使用简便的语法实现状态机的编写,提供多语言支持,支持生成图形,大大简化了状态机的编写。
真实感图形显示
真实感图形技术是指在使用显示设备绘制物体图形时,必须把三维信息经过某种投影变换,在二维的显示表面上绘制出来,使其具有真实地立体感觉。三维图形的表现形式有三种,第一种是线框图,它是通过物体的棱边和轮廓线表示物体。用线框图表示的三维图形具有二义性。第二种形式是消隐图,图中只保留形体上能看见的部分,看不见或被遮挡的部分不画出来或用虚线表示。第三种形式是用光照效果、图案纹理和颜色,使图形具有立体真实地感觉。在实际应用中,第三种形式应用最多。
在实际应用中,通常为了增加真实感,还需要包含以下图形技术:
-
提供精细的多边形模型及消隐技术。
场景的模型的多边形数量直接决定了仿真系统的精细度,对于要求较高的场合,通过导出机床的设计图纸可以得到极为真实的机床模型。但由于应用如此精细的机床模型可能会降低场景的整体性能,并且隐藏不可见零件或合并过于细小零件对场景的视觉感官差异并不大,所以在实际应用时力求精简几何模型,达到速度和画质的平衡。
另一方面,精细的多边形模型会为干涉检查带来更多的优势。对于某些较为特殊的机床,例如多轴联动的弯管机,可能需要进行某些极限情况下的仿真。这时机床零件之间的相互距离减小到一个临界值,如果多边形模型过于粗糙,则可能不能反映出正确的加工要求。
多边形模型如果用线框模型进行显示,则会显示出大量的线段,没有层次感、不便于进行观察。通过面片模型进行渲染,进行隐面消除会渲染出较为真实的图形。传统的OpenGL或DirectX提供了基于Z-buffer的隐面消除方案。OpenSceneGraph则直接在物体空间进行剔除和裁剪,完全不绘制不出现的物体,为更复杂场景提供了速度更快,质量更好的隐面消除。
-
光照模型、明暗处理
如果场景仅仅是消隐过的多边形模型,则渲染得到的多边形图形将会是缺乏立体感,看起来像是平面的对象。之所以得到这样的结果,是由于默认的光照方式使得观察者看到的只是单一的颜色。例如一个球体,则会看上去是一个均匀着色的圆。
OpenGL提供了明暗处理的模型,将光线与对象之间的相互作用分镜面反射、漫反射和半透明表面三种。为了提高渲染的速度,OpenGL引入并实现了Phong反射模型,通过设置环境光、漫反射光和镜面反射光即可得到较为真实的三维图像。实际应用中,大多数建模软件导出的三维模型的选项中都提供了导出光照的选项,结合仿真系统自身的光照系统,能够获得最佳的图像质量。
-
应用材质和贴图
材质的应用在提升整体视觉效果上占有较高的比例。人眼通过不同的材料的视觉效果能够区分材料的种类,而贴图则能提供一般无法用多边形表达的图形。OpenGL提供了设置多边形颜色、材质和进行多边形贴图的函数。通常,材质和贴图应用在比较仿真级别较高的场合,用于更为真实的交互环境。
-
|
|
|
图 3-5 数控系统典型状态机 |
有限状态机(也称"有限状态自动机")提供了一个简单、优雅的方法揭示和定义复杂系统的行为。它们同样也提供了一个易于理解、易于修改的有效实现策略。在仿真系统中,从控制高层逻辑的GUI到最底层的通讯协议,都可以应用它[16]。
有限状态机提供了一种途径,使得执行了某种动作后会将处从一种状态跳转为另一种状态。图 3-5描述了一个数控系统典型的状态机。每一个圆圈表示一个"状态"(State),箭头代表了状态之间的"转化"(Transaction),箭头上标明了状态转化所需要的"动作"(Action)。一个相同的动作在不同的状态会产生不同的效果。例如在"初始化"状态时,执行"运行",则状态依然保持在运行状态;当处于"文件已载入"的情况下,执行"运行"则会进入到"运行中"的状态,在此状态下执行"运行"则停留在"运行中";在"报警中"的状态下,执行"运行"还是回到"报警中"的状态,仅有执行"解除报警"才能回到"运行中"的状态。
为了实现有限状态机,有以下几种典型方案:嵌套switch/case语句;迁移表;状态模式。其中状态模式具有嵌套switch/case语句的效率又有迁移表的灵活性[16]。
状态模式
|
|
|
图 3-6状态模式UML |
图 3-6是状态模式的UML图,其中:



通过扩展维度转换为向量
,最后一位通常称之为w,这种4D向量称为齐次坐标。










的乘积为一个常量矩阵,Mx和Mt之间的坐标关系是固定的。通过计算公式(3-8)可以得到Mxt的值。
确定"约束关系":




代表了从My空间到Mx空间无约束的转换,此转换在模型导出时已经计算好。设此时
,可得
:






中的基本集合元素为三角形,第i个三角形的顶点用
的均值
和协方差矩阵C计算如下:


中各元素顶点在该基底三个轴上的最大值和最小值,以确定该OBB 的大小。存储一个OBB 需要15 个浮点数(表示方向的3 个基底向量共9 个浮点数和表示范围的6 个浮点数)。

矩阵
平移矩阵才能计算干涉。对于几何形状不发生变化的物件,创建一次几何信息以后,只需要重新传递当前平移矩阵即可进行干涉检查。







开始,直到
,每一步为
并传输给OpenGL
法线并传输给OpenGL
并传输给OpenGL
法线并传输给OpenGL
即可
,细分为S2段,则两段之间的弧度为
。将描述管形截面的数据进行在X正方向平移R,然后绕Y轴旋转
,n为段数。这时坐标系也进行了平移和旋转。再向平移旋转后的坐标系的X正方向平移-R即可得到弧度为
处的坐标值。对细分后的每一段都进行相同的操作,最终会得到所求弯管段。





段对应的法线可以根据公式(43)得到。




的计算为:

的计算步骤为:



