将“运动控制功能块”移植到OOP中示例

今天继续推进《将“运动控制功能块”移植到OOP中》的学习,反复学习了6,7,8三个示例。三个示例本身很简单,通过状态机实现轴多轴的时序配合,通过itfAxis和itfCommand接口实现功能调用。但是在状态机中,对于运动控制相关的Method并没有循环调用,只在状态转移时调用一次,这是如何实现真正的运动控制过程控制的还很迷糊。为了一探究竟,我突然萌生了一共奇特的想法:我计划在接下来的几天把经典的Codesys的PLCopen库重新在西门子的1500上实现,且采用AX的面向对象的设计思想。让自动化行业最具有代表性的2个平台在AX的作用下融合在一起,想想都刺激!

下面是今天学习的一些笔记,供日后参阅。

6、仓库示例

6.1 应用说明

该应用的目的是自动从货架的储藏柜中取出货物。货物存放在托盘中,可以用叉子系统取出。
仓储实例概述
图4:仓储实例概述

仓库的任务是用三个轴移动叉子来放置或取走托盘:

  • X 轴沿地面移动;
  • Y 轴移动到所需高度;
  • Z 轴将叉子移动到货架上,取走托盘。

顺序是将X轴和Y轴移动到所需位置。两个轴到达该位置后,Z轴立即移动到托盘下方的货架上,在本例中是移动1000毫米。然后,Y轴将托盘再提升100毫米,将托盘从货架上抬起,这样托盘就可以从货架上移出,并移动到所需位置进行交付。

这个示例可以用不同的方法实现。一种直接的方法是使用PLCopen第1部分功能块。另外,也可以在支持PLCopen第4部分(协调运动)的控制器中定义XYZ组,这样可以简化和优化运动

6.2 第一个编程示例(使用第1部分中的FB)

只需使用第1部分中的功能块,就可以通过以下方式实现。

 仓储示例的第一个程序*
图5: 仓储示例的第一个程序

6.3 时序图

下图显示了从仓储系统提取托盘的顺序。

仓储示例的时间图

图6:仓储示例的时间图

6.4 OOP实现

本示例用于展示ST语言的OO编程。对于PLC程序员来说,编程非常简单,后台使用的是标准PLCopen运动控制库第1部分(v2)。通常,这些FB是以循环方式调用的,这与OOP理念不太相符。如果不需要反馈(完成、错误…),那么不循环调用FB,也是可以的。通过GetCommandStatus方法,可以调用底层FB(如 MC_MoveAbsolute),并通过GetCommandStatus(通过内部调用MC_MoveAbsolute和其他实例)返回运动的状态信息(完成、错误等)。在此基础上,可以开始下一条指令。当然,这些方法的实现取决于具体的供应商。

程序使用了状态机,不同的状态反映了整个轨迹的不同阶段。通过使用变量lastCommandX、lastCommandY和lastCommandZ,程序变得非常透明(见图7:上部为变量声明部分,下部为以CASE指令形式实现的状态机)。由此产生的时序图如图8所示。

PROGRAM WarehosingExample
VAR
    AxisX:itfAxis;
    AxisY:itfAxis;
    AxisZ:itfAxis;
    stateOOP:INT;
    lastCommandX:itfCommand;
    lastCommandY:itfCommand;
    lastCommandZ:itfCommnad;
    stepOn:BOOL:= FALSE;
    targetPosX:REAL:= 400;
    targetPosY:REAL:= 600;
END_VAR

CASE stateOOP OF
    0:;
    10: //init
        AxisX.Power(Enable := TRUE,EnablePositive := TRUE,EnableNegative := TRUE);
        AxisY.Power(Enable := TRUE,EnablePositive := TRUE,EnableNegative := TRUE);
        AxisZ.Power(Enable := TRUE,EnablePositive := TRUE,EnableNegative := TRUE);
        
        IF stepOn THEN
            stateOOP := stateOOP + 10;
        END_IF
    20: // start movement in XY
        lastCommnadX := AxisX.MoveAbsolute(Position := targetX,Velocity := 40,Acceleration := 0,Deceleration:= 0,Jerk := 0,Direction := 0,BufferMode := MC_BUFFER_MODE.mcAborting);
        lastCommnadY := AxisY.MoveAbsolute(Position := targetY,Velocity := 40,Acceleration := 0,Deceleration:= 0,Jerk := 0,Direction := 0,BufferMode := MC_BUFFER_MODE.mcAborting);
        stateOOP := stateOOP + 10;
    30: //wait till movement is finished to 'forkIn'
        IF lastCommnadX.Done AND lastCommnadY.Done THEN
            lastCommnadZ := AxisZ.MoveRelative(Distance := 100,Velocity := 20,Acceleration := 0,Deceleration:= 0,Jerk := 0,BufferMode := MC_BUFFER_MODE.mcAborting);
             stateOOP := stateOOP + 10;
        END_IF
    40:
        //lift pallet if fork in
        IF lastCommnadZ.Done THEN
            lastCommnadY := AxisY.MoveRelative(Distance := 100,Velocity := 20,Acceleration := 0,Deceleration:= 0,Jerk := 0,BufferMode := MC_BUFFER_MODE.mcAborting);
            stateOOP := stateOOP + 10;
        END_IF
    50:
        //fork out with pallet if forked in is done
         IF lastCommnadY.Done THEN
            lastCommnadZ :=  AxisZ.MoveAbsolute(Position := 0,Velocity := 40,Acceleration := 0,Deceleration:= 0,Jerk := 0,Direction := 0,BufferMode := MC_BUFFER_MODE.mcAborting);
            stateOOP := stateOOP + 10;
        END_IF
    60:
        //move to delivery if fork out is done
        IF lastCommnadZ.Done THEN
            lastCommnadX := AxisX.MoveAbsolute(Position := 0,Velocity := 40,Acceleration := 0,Deceleration:= 0,Jerk := 0,Direction := 0,BufferMode := MC_BUFFER_MODE.mcAborting);
            lastCommnadY := AxisY.MoveAbsolute(Position := 0,Velocity := 40,Acceleration := 0,Deceleration:= 0,Jerk := 0,Direction := 0,BufferMode := MC_BUFFER_MODE.mcAborting);
            stateOOP := stateOOP + 10;
        END_IF
    70:
        //wait till finished
        IF lastCommnadX.Done AND lastCommnadY.Done THEN
            stateOOP := stateOOP + 10;
        END_IF
    80:
        //ready
        ;
END_CASE

图7:OOP程序示例
用OOP实现的仓库示例时序图
图8:用OOP实现的仓库示例时序图

7、标签示例

7.1 应用描述

任务是将标签贴在产品的特定位置上。该应用有两个驱动器,一个用于通过传送带送入产品,另一个用于送入标签并将标签贴在产品上。贴标过程由位置检测传感器触发(参见图9:顶部)。从检测到产品到开始贴标,会有一个延迟时间,取决于传送带的速度、传感器的位置和标签在产品上的位置。
在这里插入图片描述

图9:贴标机

7.2 编程示例

本示例展示了用编程语言FBD解决这一任务的方法,如图10;

贴标机程序示例
图10:贴标机程序示例

两个轴均以相同的速度设定值移动。TON的延迟时间根据传感器距离和速度计算得出。一个贴标步骤结束后,LabelDrive再次停止并等待下一次触发,同时传送带继续移动。

OOP实现

使用编程语言ST和引入的运动控制OOP元素,将标签示例转换为OOP。与仓库示例类似,后台使用标准PLCopen运动控制库第1部分(v2),程序使用状态机反映过程的不同状态,程序如下:

PROGRAM LabelingExample
VAR 
    //Hardware defineition
    LabelDrive:Axis;
    Coveyor:Axis;
    LabelLength:REAL:=100;
    SensorDistance:REAL:=10;
    Velocity:REAL:= 5;
    ProductDetection:BOOL:= FALSE;
    DelayTimer:TON;
    
    //States
    CoveyorState:INT := 0;
    LabelDriveState:INT := 0;
    
    //Control
    Start:BOOL := FALSE;
    
    //Commnads
    ConveyorMove : itfCommand;
    LabelDriveMove : itfCommand;

END_VAR

//Run the timer. As it is a classic FB it should be called every cycle
DelayTimer(IN:= ProductDetection,PT:= INT_TO_TIME(REAL_TO_INT(SensorDistance * 1000 / Velocity)));

CASE CoveyorState OF 
    0: //Disabled
        IF Start THEN
            CoveyorState := CoveyorState + 1;
        END_IF
    1: // Power On
        Coveyor.Power(Enable := TRUE ,EnablePositive := TRUE, EnableNegative := TRUE);
        IF Coveyor.Status = AXIS_STATUS.Standstill THEN
            CoveyorState := CoveyorState + 1;
        END_IF
        
    2: //Wait for LabelDrive
        IF LabelDrive.Status = AXIS_STATUS.Standstill THEN
            ConveyorMove := Coveyor.MoveVelocity(Velocity := Velocity,Acceleration:= 0,Deceleration:= 0,Jerk := 0, Direction := MC_Direction.mcPositiveDirection,BufferMode := MC_BUFFER_MODE.mcAborting );
            CoveyorState := CoveyorState + 1;
        END_IF
    3: // Moving
        IF NOT Start THEN
            ConveyorMove := Coveyor.Halt(Deceleration := 0,Jerk := 0, BufferMode := MC_BUFFER_MODE.mcAborting);
            CoveyorState := CoveyorState + 1;
        END_IF
    4: // Stoping
        IF ConveyorMove.Done THEN
            CoveyorState := CoveyorState + 1;
        END_IF
    5: // Power off
        Coveyor.Power(Enable := FALSE ,EnablePositive := FALSE, EnableNegative := FALSE);
        IF  Coveyor.Status = AXIS_STATUS.Disabled THEN
            CoveyorState := 0;
        END_IF
END_CASE

CASE LabelDriveState OF 
    0: //Disabled
        IF Start THEN
            LabelDriveState := LabelDriveState + 1;
        END_IF
    1: // Power On
        LabelDrive.Power(Enable := TRUE ,EnablePositive := TRUE, EnableNegative := TRUE);
        IF LabelDrive.Status = AXIS_STATUS.Standstill THEN
            LabelDriveState := LabelDriveState + 1;
        END_IF
        
    2: //Wait for Coveyor
        IF Coveyor.Status = AXIS_STATUS.Standstill THEN
            LabelDriveState := LabelDriveState + 1;
        END_IF
    3: // Waitf for product
        IF NOT Start THEN
            LabelDriveState := 5;
        END_IF
        
        IF DelayTimer.Q THEN
            LabelDriveMove := LabelDrive.MoveRelative(Distance := LableLength,Velocity := Velocity,Acceleration:= 0,Deceleration:= 0,Jerk := 0,BufferMode := MC_BUFFER_MODE.mcAborting );
            LabelDriveState := LabelDriveState + 1;
        
    4: // Wait for lable to be transfered
        IF LabelDriveMove.Done AND NOT DelayTimer.Q THEN
            LabelDriveState := 3;
        END_IF
    5: // Power off
        LabelDrive.Power(Enable := FALSE ,EnablePositive := FALSE, EnableNegative := FALSE);
        IF  LabelDriver.Status = AXIS_STATUS.Disabled THEN
            LabelDriveState := 0;
        END_IF
END_CASE

标签示例时序图
图13:标签示例时序图

8、带凸轮和齿轮的示例

8.1 应用说明

这是一个简单的应用程序演示,其中一个主轴以固定速度运动,第二个轴作为CAM从动轴,第三个齿轮从动轴通过OOP实现轴同步。

8.2 经典编程示例

共有三个驱动器,即MasterDrive、CamDrive和GearDrive。第一步是接通电源。通过CamTableSelect和CamIn将凸轮轴与主驱动器连接。齿轮传动装置通过GearIn连接。一旦两个从动轴和主轴都准备就绪(与主轴同步和齿轮同步),主轴开始以速度运动,两个从动轴也随之运动。FBD中该示例的经典实现如图 14 所示。

三驱动同步示例的经典FBD实现
图14:三驱动同步示例的经典FBD实现

8.3 OOP实现

使用OOP的同步示例使用本文介绍的接口、ENUMS和STRUCT。主程序的声明部分如下图所示:它包含硬件定义、驱动器状态以及使两个从动轴与主轴同步的命令。为实现同步,定义的接口itfSynchronizedAxisCommand用于控制凸轮驱动器(CamIn)和齿轮驱动器(GearIn)。

该示例说明,使用新定义接口提供的命令来实现同步驱动相对简单。

PROGRAM CamGearExample
VAR
    //Hardware definitions
    MasterDrive:Axis;
    CamDrive:Axis;
    GearDrive:Axis;
    Table:CamTable;
    TableData:MC_CAM_REF;
    Velocity:REAL:= 5;
    
    //State
    MasterState :INT := 0;
    CamState:INT := 0;
    GearState:INT := 0;
    
    //Control
    Start :BOOL := FALSE;
    
    //Commands
    MasterMove :itfCommand;
    TableSet:itfCommand;
    CmaMove:itfSynchronizeAxisCommand;
    GearMove:itfSynchronizeAxisCommand;
END_VAR

CASE MasterState OF 
    0: //Disable
        IF Start THEN
            MasterState := MasterState + 1;
        END_IF
    1: //Power On
        MasterDrive.Power(Enable := TRUE,EnablePositive := TRUE,EnableNegative := TRUE);
        IF MasterDrive.Status = AXIS_STATUS.Standstill THNE
            MasterState := MasterState + 1;
        END_IF
    2: //Wait for other drives
        IF CamMove.InSync AND GearMove.InSync THEN
            MasterMove := MasterDrive.MoveVelocity(Velocity := Velocity,Acceleration := 0,Deceleration :=0,Jerk := 0,Direction := 0,BufferMode := MC_BUFFER_MODE.mcAborting);
            MasterState := MasterState + 1;
        END_IF
    3: //Moving
        IF NOT Start THEN
            MasterMove := MasterDrive.Halt(Deceleration := 0,Jerk := 0,BufferMode := MC_BUFFER_MODE.mcAborting);
            MasterState := MasterState + 1;
        END_IF
    4:  //Stoping
        IF NOT MasterMove.Done THEN
            MasterState := MasterState + 1; 
        END_IF
    5:  //Power off
        MasterDrive.Power(Enable := FALSE,EnablePositive := FALSE,EnableNegative := FALSE);
        IF MasterDrive.Status = AXIS_STATUS.Disable THNE
            MasterState := 0;
        END_IF
END_CASE

CASE CamState OF 
    0: //Disable
        IF Start THEN
            CamState := CamState + 1;
        END_IF   
    1: //Power On
        CamDrive.Power(Enable := TRUE,EnablePositive := TRUE,EnableNegative := TRUE);
        IF CamDrive.Status = AXIS_STATUS.Standstill AND MasterDrive.Status = AXIS_STATUS.Standstill THNE
            TableSet := Table.Select(CamTable := TableData,Periodic:= TRUE,MasterAbsolute := TRUE,SlaveAbsolute := TRUE);
            CamState := CamState + 1;
        END_IF
    2:  //Set CAM Table
        IF TableSet.Done THEN
            CamMove := CamDrive.CamIn(Master:= MasterDrive,MasterOffset := 0,SlaveOffset := 0,MasterScaling := 1 ,MasterStartDistance :=     0,MasterSyncPosition := 0,StartMode := MC_StartMode.mcRelative,MasterValueSource := MC_Source.mcSetValue,CameTable:=         Table,BUfferMode := MC_BUFFER_MODE.mcAborting);
            CamState := CamState + 1;
        END_IF
    3:  //Synchronized
        IF NOT Start THEN
            CamDrive.Release();
            CamState := CamState + 1;
        END_IF
    4:  //Power Off
        CamDrive.Power(Enable := FALSE,EnablePositive := FALSE,EnableNegative := FALSE);
        IF CamDrive.Status = AXIS_STATUS.Disable THNE
            CamState := 0;
        END_IF
END_CASE

CASE GearState OF 
    0: //Disable
        IF Start THEN
            GearState := GearState + 1;
        END_IF   
    1: //Power On
        GearDrive.Power(Enable := TRUE,EnablePositive := TRUE,EnableNegative := TRUE);
        IF GearDrive.Status = AXIS_STATUS.Standstill AND MasterDrive.Status = AXIS_STATUS.Standstill THNE
            GearMove := GearDrive.GearIn(Master := MasterDrive,Ratio:= 2,MasterValueSource := MC_Source.mcSetValue,Acceleration := 0,Deceleration := 0,Jerk:=0,BufferMode := MC_BUFFER_MODE.mcAborting);
            GearState := GearState + 1;
        END_IF
    2:  //Synchronized
        IF NOT Start THEN
            GearDrive.Release();
            GearState := GearState + 1;
        END_IF
    3:  //Power Off
        GearDrive.Power(Enable := FALSE,EnablePositive := FALSE,EnableNegative := FALSE);
        IF GearDrive.Status = AXIS_STATUS.Disable THNE
            GearState := 0;
        END_IF    
END_CASE

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值