跳转至

运动模式

1.1 简介

运动模式是指规划一个或多个轴运动的轨迹。运动控制卡支持的运动模式包括单轴点位运动、单轴连续运动、单轴手轮运动、同步运动和插补运动等。用户根据实际需求选择相应的运动模式。

1.2 单轴点位运动模式

1.2.1 Trap运动

1.2.1.1 指令列表

点位(Trap)运动模式指令列表

指令 说明
GTN_PrfTrap 设置指定轴为Trap运动模式。
GTN_SetTrapPrm 设置Trap运动模式下的运动参数。
GTN_GetTrapPrm 读取Trap运动模式下的运动参数。
GTN_SetPosEx 设置Trap运动目标位置。
GTN_GetPosEx 读取Trap运动目标位置。
GTN_SetVel 设置Trap运动目标速度。
GTN_GetVel 读取Trap运动目标速度。
GTN_UpdatePro 启动运动。
GTN_GetTrapSts 读取Trap运动到位标志。
GTN_ClearTrapSts 清除Trap运动到位标志。
GTN_SetPosArray 批量设置Trap运动目标位置。
GTN_GetPosArray 批量读取Trap运动目标位置。
GTN_SetVelArray 批量设置Trap运动目标速度。
GTN_SetTrapPrmArray 批量设置Trap运动参数。

1.2.1.2 重点说明

每一个轴在规划静止时都可以设置为Trap运动。在Trap运动模式下,各轴可以独立设置目标位置、目标速度、加速度、减速度、起跳速度、平滑时间等运动参数,能够独立运动或停止。

1. 如何切换到Trap运动?

用户必须要调GTN_PrfTrap,才能将指定轴设定为Trap模式,运动控制器默认Trap运动模式。

1. 如何设置Trap运动参数?

用户调用GTN_SetTrapPrm设置Trap运动的运动参数,调用GTN_SetPosEx设置Trap运动的目标位置,调用GTN_SetVel设置Trap运动的目标速度。

3. 如何启动Trap运动?

用户调用GTN_Update指令启动Trap运动,之后控制器根据设定的运动参数自动生成相应的梯形曲线速度规划,并且在运动过程中可以随时修改目标位置和目标速度。

4. 如何使Trap运动更加平稳?

通过GTN_SetTrapPrm指令设定Trap运动的平滑时间,能够得到平滑的速度曲线,从而使加减速过程更加平稳,如图所示。

点位运动速度曲线
Trap例程
// Trap.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:Trap点到点运动示例
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)规划运动
//           (3)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                         // 指令返回值
    short core;                        // 需要执行例程的运动控制器核号
    short axis;                        // 需要初始化的轴起始索引号
    short axisCount;                   // 需要初始化的轴数量,从轴起始索引号开始算起
    TTrapPrm trapPrm;                  // 点位运动参数
    double prfPos;                     // 实时读取的规划位置
    double moveVel;                    // 目标速度
    long axisSts;                      // 轴状态
    long targetPos;                    // 目标位置
    unsigned long clock;               // 控制器时钟
    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 8;
    rtn = InitMc(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*rtn = GTN_AxisOn(CORE,TRAP_MOTION_AXIS_NUMBER);
    if (0 != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }*/

    // 将轴设置为点到点运动模式
    rtn = GTN_PrfTrap(core,axis);
    if (0 != rtn)
    {
        return CommandHandler("GTN_PrfTrap",rtn);
    }

    // 设置点到点运动的运动参数,包括加速度、减速度、平滑时间、目标速度、目标位置
    // 读取轴点到点运动的运动参数
    rtn = GTN_GetTrapPrm(core,axis,&trapPrm);
    if (0 != rtn)
    {
        return CommandHandler("GTN_GetTrapPrm",rtn);
    }

    // 设置轴点到点运动的运动参数        
    trapPrm.acc = 1;            // 运动加速度(acc)          单位:脉冲/ms^2
    trapPrm.dec = 1;            // 运动减速度(dec)          单位:脉冲/ms^2
    trapPrm.velStart = 0;       // 运动起跳速度(velStart)   单位:脉冲/ms
    trapPrm.smoothTime = 1;     // 运动平滑时间(smoothTime) 单位:ms
    rtn = GTN_SetTrapPrm(core,axis,&trapPrm);
    if (0 != rtn)
    {
        return CommandHandler("GTN_SetTrapPrm",rtn);
    }

    // 设置轴点到点运动的运动速度
    moveVel = 1;              // 运动速度                   单位:脉冲/ms
    rtn = GTN_SetVel(core,axis,moveVel);
    if (0 != rtn)
    {
        return CommandHandler("GTN_SetVel", rtn);
    }

    // 设置轴点到点运动的目标位置
    targetPos = 1000;           // 目标位置                 单位:脉冲
    rtn = GTN_SetPos(core,axis,targetPos);
    if (0 != rtn)
    {
        return CommandHandler("GTN_SetPos",rtn);
    }

    // 启动轴点到点运动
    // 启动轴点到点运动,该指令按bit位启动相应轴运动,可实现多轴同时启动
    rtn = GTN_Update(1, 1<<(axis - 1));
    if (0 != rtn)
    {
        return CommandHandler("GTN_Update",rtn);
    }

    // 读取轴运动状态
    axisCount = 1;
    do
    {
        // 读取轴的状态
        rtn = GTN_GetSts(core,axis,&axisSts,axisCount,&clock);
        if(0 != rtn)
        {
            CommandHandler("GTN_GetSts",rtn);
        }
        // 读取轴的规划位置
        rtn =  GTN_GetPrfPos(core,axis,&prfPos,axisCount,&clock);
        if(0 != rtn)
        {
            CommandHandler("GTN_GetPrfPos",rtn);
        }

        //打印运动轴的状态及其规划位置值
        printf("轴Axis  %d ,轴状态sts=0x%-10lx 规划位置prfPos=%-10.1lf \r", axis,axisSts,prfPos);

        // 规划器处于运动状态时bit10为1
    }while( 0x400 == (axisSts & 0x400) ); // 规划到位后退出循环

    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*rtn = GTN_AxisOff(CORE,TRAP_MOTION_AXIS_NUMBER);
    if (0 != rtn)
    {
        return CommandHandler("GTN_AxisOff", rtn);
    }*/

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    printf("Press Any Key To Exit !\n");
    getchar();

    return 0;
}

1.2.2 MoveAbsolute运动

1.2.2.1 指令列表

MoveAbsolute运动模式指令列表

指令 说明
GTN_MoveAbsoluteEx 执行MoveAbsoluteEx运动。
GTN_GetMoveAbsoluteEx 读取MoveAbsoluteEx运动参数。

1.2.2.2 重点说明

MoveAbsolute运动从当前位置和当前速度开始,按照设定的最大速度和加速度运动到目标位置,到达目标位置以后运动停止。

1. 如何切换至MoveAbsolute?

用户调用GTN_MoveAbsoluteEx指令后,将指定轴从其他运动模式(除插补运动)立即切换到MoveAbsolute运动,该切换无需等待其他运动停止。

2. 如何设置并启动MoveAbsolute运动?

MoveAbsolute运动通过GTN_MoveAbsoluteEx指令设置,并通过该指令立即启动MoveAbsolute运动。

MoveAbsolute运动可以设置不同的加速度和减速度,实现非对称加减速。通过设置百分比参数,可以将梯形加减速转换为S曲线加减速。

S曲线百分比解释

S曲线百分比=2ta/(2ta+te)*100%。
当百分比为0时是梯形曲线加减速。
当百分比为100时没有匀加速段(te=0)。

MoveAbsolute运动还可以设置起点速度、起点加速度、终点速度、终点加速度,如图所示。

MoveAbsolute模式S曲线百分比示意图
上图参数解析

velStart为起点速度,velEnd为终点速度,velStart和velEnd可以不相等。只有在静止状态下调用GTN_MoveAbsoluteEx时才可以设置起点速度。
accStartPercent为起点加速度百分比,计算公式为:

起点加速度=acc x accStartPercent/100.0
decEndPercent为终点加速度百分比,计算公式为:
终点加速度= dec × decEndPercent/100.0decEndPercent × dec

MoveAbsolute例程
// MoveAbsolute.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:MoveAbsolute点到点运动示例
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)规划运动
//           (3)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                         // 指令返回值
    short core;                        // 需要执行例程的运动控制器核号
    short axis;                        // 需要初始化的轴起始索引号
    short axisCount;                   // 需要初始化的轴数量,从轴起始索引号开始算起
    TMoveAbsolutePrmEx moveAbsolutePrm;// 运动参数
    double prfPos;                     // 实时读取的规划位置
    long axisSts;                      // 轴状态
    unsigned long clock;               // 控制器时钟

    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 8;
    rtn = InitMc(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*rtn = GTN_AxisOn(CORE,TRAP_MOTION_AXIS_NUMBER);
    if (0 != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }*/

    // 加速度acc、减速度dec不相等时,为非对称的加减速
    moveAbsolutePrm.pos = 1500;             // 目标位置,相对于原点的绝对位置,单位:pulse。
    moveAbsolutePrm.vel = 10;               // 最大速度,单位:pulse/ms。
    moveAbsolutePrm.acc = 0.1;              // 加速度,单位:pulse/ms2。
    moveAbsolutePrm.dec = 0.1;              // 减速度,单位:pulse/ms2。
    moveAbsolutePrm.percent = 100;          // S曲线百分比,即加速段中,变加速的部分占整个加速时间的百分比,取值范围:[0, 100]。
    moveAbsolutePrm.velStart = 0;           // 起点速度,单位:pulse/ms。
    moveAbsolutePrm.velEnd = 0;             // 终点速度,单位:pulse/ms。
    moveAbsolutePrm.accStartPercent = 0;    // 起始加速度占加速度acc的百分比,既运动开始时的起始加速度,取值范围:[0, 100]。
    moveAbsolutePrm.decEndPercent = 0;      // 结束减速度占减速度acc的百分比,既运动结束时的结束减速度,取值范围:[0, 100]。
    rtn = GTN_MoveAbsoluteEx(core,axis,&moveAbsolutePrm);
    if(0 != rtn)
    {
        CommandHandler("GTN_GetSts",rtn);
    }

    // 读取轴运动状态
    axisCount = 1;
    do
    {
        // 读取轴的状态
        rtn = GTN_GetSts(core,axis,&axisSts,axisCount,&clock);
        if(0 != rtn)
        {
            CommandHandler("GTN_GetSts",rtn);
        }
        // 读取轴的规划位置
        rtn =  GTN_GetPrfPos(core,axis,&prfPos,axisCount,&clock);
        if(0 != rtn)
        {
            CommandHandler("GTN_GetPrfPos",rtn);
        }

        //打印运动轴的状态及其规划位置值
        printf("轴Axis  %d ,轴状态sts=0x%-10lx 规划位置prfPos=%-10.1lf \r", axis,axisSts,prfPos);

        // 规划器处于运动状态时bit10为1
    }while( 0x400 == (axisSts & 0x400) ); // 规划到位后退出循环

    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*rtn = GTN_AxisOff(CORE,TRAP_MOTION_AXIS_NUMBER);
    if (0 != rtn)
    {
        return CommandHandler("GTN_AxisOff", rtn);
    }*/

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    printf("Press Any Key To Exit !\n");
    getchar();

    return 0;
}

1.2.3 PT运动

1.2.3.1 指令列表

PT运动模式指令列表

指令 说明
GTN_PrfPt 设置指定轴为PT运动模式。
GTN_PtSpace 查询PT运动模式指定FIFO的剩余空间。
GTN_PtData 向PT运动模式指定FIFO增加数据。
GTN_PtClear 清除PT运动模式指定FIFO中的数据。
运动状态下该指令无效。
动态模式下该指令无效。
GTN_SetPtLoop 设置PT运动模式循环执行的次数。
动态模式下该指令无效。
GTN_GetPtLoop 查询PT运动模式循环执行的次数。
动态模式下该指令无效。
GTN_PtStartPro 启动PT运动。
GTN_SetPtMemory 设置PT运动模式的缓存区大小。
GTN_GetPtMemory 读取PT运动模式的缓存区大小。

1.2.3.2 重点说明

PT模式是通过由时间和位置共同组成的数组来描述运动规律的运动模式,用户需要将待规划的速度曲线分割成若干段来进行描述,如图所示的速度规划曲线中,整个速度曲线被分割成5段。

PT运动速度曲线
上图参数解释说明

其中,第1段起点速度为0,经过时间T1后,运动到目标位置P1,因此第1段的终点速度为v1=2P1/T1。第2段起点速度为v1,经过时间T2后,运动到目标位置P2,因此第2段的终点速度为v2=2P2/T2-v1。第3、4、5段依此类推。因此,当处于PT模式下时,用户需输入每一个运动段所需运动时间和目标位置。

提示

在描述一次完整的PT运动时,第1段的起点位置和时间被假定为0。压入控制器的数据为位置点,即相对于第1段的起点的绝对值,而不是每段位移长度。位置的单位是脉冲(pulse),时间单位是毫秒(ms)。

PT模式在实现任意速度规划方面非常具有优势。用户将任意的速度规划曲线分割为足够密的小段,用户只需要给出每段所需时间和位置点,运动控制卡会计算段内各点的速度,生成一条连续的速度曲线。为了得到光滑的速度曲线,可以增加速度曲线的分割段数。

1. 如何切换到PT模式?

用户必须要调用GTN_PrfPt,才能将指定轴设定为PT模式。

2. 认识PT模式的数据段类型。如何向PT模式的FIFO中写入数据段?

(1) PT模式的数据段有3种类型。

(2) PT_SEGMENT_NORMAL表示普通段,FIFO中第1段的起点速度为0,从第2段起每段的起点速度等于上一段的终点速度。

(3) PT_SEGMENT_EVEN表示匀速段,FIFO中各段的段内速度保持不变,段内速度=段内位移/段内时间。如图所示。

PT模式匀速段类型

(4) PT_SEGMENT_STOP表示停止段,该段的终点速度为0,起点速度根据段内位移和段内时间计算得到,和上一段的终点速度无关。如图所示。

PT模式停止段类型

(5) 调用GTN_PtData将数据段写入指定FIFO中。

3. 如何设置PT循环次数?

如果轴的运动是周期性的,用户可以只写入一个周期的运动规划数据段到FIFO中,然后设定循环次数,即可实现周期性的PT运动。调用指令GTN_SetPtLoop即可。

4. PT模型下,如何使用FIFO?

(1) PT模式下,有2个FIFO用来存放数据,分别为FIFO1和FIFO2。PT具有2种FIFO使用模式:静态模式和动态模式。

(2) 静态模式下,可以选择启动其中一个FIFO,运动完成以后规划停止。控制卡不会清除FIFO中的数据,用户可以重复使用FIFO中的数据。静止状态下调用GTN_PtClear指令可以清空指定FIFO。在运动状态下不能清空正在使用的FIFO,但可以清除没有在使用的FIFO。

(3) 动态模式下,不可以选择启动哪一个FIFO,控制卡会启用两个FIFO(动态模式下,PT指令中选择FIFO的参数都是无效的)。当一个FIFO中的数据用完以后会自动清空,同时切换到另一个FIFO,此时可以向控制卡发送新的PT数据。当2个FIFO中的数据都用完以后规划停止。为了避免数据跑空而异常停止,必须在2个FIFO中的数据都用完之前及时发送新的数据。调用GTN_PtSpace指令可以查询剩余多少数据空间。

(4) 调用GTN_SetPtMemory可以设置FIFO的大小,根据PT模式,静态模式下有32段和1024段两种选择,默认为32段;动态模式下有64段和2048段两种选择。默认为64段。

提示

在切换到PT模式时(调用指令GTN_PrfPt),应设置FIFO为“静态模式”或“动态模式”。运动时不能修改FIFO的使用模式。

PT静态FIFO示例

该例程生成一段梯形曲线速度规划,一共三段数据段,如表所示。

PT静态FIFO例程数据段

第一段 第二段 第三段
位置点(pos) 1024 2048 1024
时间(time) 1024 1024 1024
数据段类型 普通段 普通段 普通段
PT模式梯形曲线速度规划如图所示。
PT梯形曲线速度规划
// PtStatic.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "math.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:PT静态FIFO运动示例
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)规划运动
//           (3)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                         // 指令返回值
    short core;                        // 需要执行例程的运动控制器核号
    short axis;                        // 需要初始化的轴起始索引号
    short axisCount;                   // 需要初始化的轴数量,从轴起始索引号开始算起
    double prfPos;                     // 实时读取的规划位置
    long axisSts;                      // 轴状态
    short space;                       // PT FIFO空间剩余数量
    short ptFifo = 0;
    double pos = 0;
    long time = 0;
    unsigned long clock;

    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 8;
    rtn = InitMc(core,axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*rtn = GTN_AxisOn(CORE,TRAP_MOTION_AXIS_NUMBER);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }*/

    // 将该轴设为PT运动规划模式
    rtn = GTN_PrfPt(core,axis,PT_MODE_STATIC);
    if(CMD_SUCCESS != rtn)
    {
        CommandHandler("GTN_PrfPt",rtn);
    }

    // 清空PT的FIFO
    rtn = GTN_PtClear(core,axis,ptFifo);

    // 查询PT模式FIFO的剩余空间
    rtn =GTN_PtSpace(core,axis,&space,ptFifo);
    if(CMD_SUCCESS != rtn)
    {
        CommandHandler("GTN_PtClear",rtn);
    }

    // 向FIFO中增加运动数据
    pos = 1024;
    time = 1024;
    rtn = GTN_PtData(core,axis, pos,time,PT_SEGMENT_NORMAL,ptFifo);
    if(CMD_SUCCESS != rtn)
    {
        CommandHandler("GTN_PtClear",rtn);
    }

    // 查询PT模式FIFO的剩余空间
    rtn = GTN_PtSpace(core,axis, &space,ptFifo);
    if(CMD_SUCCESS != rtn)
    {
        CommandHandler("GTN_PtSpace",rtn);
    }
    printf("GTN_PtSpace()=%d\tspace=%d\n", rtn, space);

    // 向FIFO中增加运动数据
    pos += 2048;
    time += 1024;
    rtn = GTN_PtData(core,axis,pos,time,PT_SEGMENT_NORMAL,ptFifo);
    if(CMD_SUCCESS != rtn)
    {
        CommandHandler("GTN_PtData",rtn);
    }

    // 查询PT模式FIFO的剩余空间
    rtn = GTN_PtSpace(core,axis, &space,ptFifo);
    if(CMD_SUCCESS != rtn)
    {
        CommandHandler("GTN_PtClear",rtn);
    }
    printf("GTN_PtSpace()=%d\tspace=%d\n", rtn, space);

    // 向FIFO中增加运动数据
    pos += 1024;
    time += 1024;
    rtn = GTN_PtData(core,axis, pos,time,PT_SEGMENT_NORMAL,ptFifo);
    if(CMD_SUCCESS != rtn)
    {
        CommandHandler("GTN_PtClear",rtn);
    }

    // 启动PT运动
    rtn = GTN_PtStart(core,1<<(axis-1));
    if(CMD_SUCCESS != rtn)
    {
        CommandHandler("GTN_PtClear",rtn);
    }

    // 读取轴运动状态
    axisCount = 1;
    do
    {
        // 读取轴的状态
        rtn = GTN_GetSts(core,axis,&axisSts,axisCount,&clock);
        if(0 != rtn)
        {
            CommandHandler("GTN_GetSts",rtn);
        }
        // 读取轴的规划位置
        rtn =  GTN_GetPrfPos(core,axis,&prfPos,axisCount,&clock);
        if(0 != rtn)
        {
            CommandHandler("GTN_GetPrfPos",rtn);
        }

        //打印运动轴的状态及其规划位置值
        printf("轴Axis  %d ,轴状态sts=0x%-10lx 规划位置prfPos=%-10.1lf \r", axis,axisSts,prfPos);

        // 规划器处于运动状态时bit10为1
    }while( 0x400 == (axisSts & 0x400) ); // 规划到位后退出循环

    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*rtn = GTN_AxisOff(CORE,TRAP_MOTION_AXIS_NUMBER);
    if (0 != rtn)
    {
        return CommandHandler("GTN_AxisOff", rtn);
    }*/

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    printf("\nPress Any Key To Exit !\n");
    getchar();

    return 0;
}

PT动态FIFO示例

该例程生成一段正弦曲线速度规划,周期为1s,循环次数为2。将该正弦曲线分割成若干小线段,每段时间间隔为0.016s。计算每个小线段的时间和位置点,传入FIFO中。由于数据段较多,使用PT的动态FIFO模式。在存入数据段的同时不断检查FIFO是否已满;当两个FIFO都满了时,启动运动;当其中一个FIFO中的数据遍历完了,控制卡将清空这个FIFO,如果此时查询到有剩余空间,就继续存入后面的数据段;依次下来,直到整条描述正弦曲线的小线段全部遍历完。PT模式正弦曲线速度规划如图所示。

PT模式正弦曲线速度规划
// PtDynamic.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "math.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:PT动态FIFO运动示例
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)规划运动
//           (3)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                         // 指令返回值
    short core;                        // 需要执行例程的运动控制器核号
    short axis;                        // 需要初始化的轴起始索引号
    short axisCount;                   // 需要初始化的轴数量,从轴起始索引号开始算起
    double prfPos;                     // 实时读取的规划位置
    double prfVel;                     // 实时读取的规划速度
    long axisSts;                      // 轴状态
    short space;                       // PT FIFO空间剩余数量
    short loop = 1;                     // 循环次数
    short A = 50;                       // 正弦速度曲线的幅值, 单位:脉冲
    short T = 1;                        // 正弦速度曲线的周期,单位:s
    double timeDelta = 0.016;           // 时间增量,          单位:s
    short loopMax = 2;                  // 循环最大次数
    double PI = 3.1415926;
    short ptFifo = 0;
    double pos = 0;
    double vel = 0;
    double velPre = 0;
    double time = 0;
    short start = 0;


    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 8;
    rtn = InitMc(core,axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*rtn = GTN_AxisOn(CORE,TRAP_MOTION_AXIS_NUMBER);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }*/

    // 将该轴设为PT运动规划模式
    rtn =GTN_PrfPt(core,axis, PT_MODE_DYNAMIC);
    if(CMD_SUCCESS != rtn)
    {
        CommandHandler("GTN_PrfPt",rtn);
    }

    // 清空PT的FIFO
    rtn =GTN_PtClear(core,axis,ptFifo);
    if(CMD_SUCCESS != rtn)
    {
        CommandHandler("GTN_PtClear",rtn);
    }

    while(1)
    {
        // 查询PT模式FIFO的剩余空间
        rtn =GTN_PtSpace(core,axis, &space,ptFifo);
        if(CMD_SUCCESS != rtn)
        {
            CommandHandler("GTN_PtSpace",rtn);
        }

        if( space> 0 )
        {
            // 时间,单位:s
            time += timeDelta;

            // 计算段末速度,单位: 脉冲/s
            vel = A*sin((2*PI)/T*time);

            // 计算段内位移, 单位:脉冲
            pos += 1000*(vel+velPre)*timeDelta/2;

            velPre = vel;

            if(time<loop*T)
            {
                // 发送新数据.时间转换成ms单位,位置也同比放大1000倍
                rtn =GTN_PtData(core,axis,pos,(long)(time*1000),PT_SEGMENT_NORMAL,ptFifo);
                if(CMD_SUCCESS != rtn)
                {
                    return CommandHandler("GTN_PtData",rtn);
                }
            }
            else
            {
                // 发送终点数据,时间转换成ms单位,位置也同比放大1000倍
                rtn =GTN_PtData(core,axis, pos,loop*T*1000, PT_SEGMENT_STOP,ptFifo);
                if( CMD_SUCCESS != rtn)
                {
                    return CommandHandler("GTN_PtData",rtn);
                }

                pos = 0;
                time = loop*T;
                velPre = 0;
                ++loop;
                if( loop>loopMax )
                {
                    break;
                }
            }
        }
        else if( 0 == start )
        {
            // 启动PT运动
            rtn =GTN_PtStart(core,1<<(axis-1));
            if(CMD_SUCCESS != rtn)
            {
                return CommandHandler("GTN_PtStart",rtn);
            }
            start = 1;
        }

        // 读取轴的状态
        rtn =GTN_GetSts (core,axis, &axisSts);
        if(CMD_SUCCESS != rtn)
        {
            return CommandHandler("GTN_GetSts",rtn);
        }

        // 读取轴的规划位置
        rtn = GTN_GetPrfPos (core,axis, &prfPos);
        if(CMD_SUCCESS != rtn)
        {
            return CommandHandler("GTN_GetPrfPos",rtn);
        }

        // 读取轴的规划速度
        rtn = GTN_GetPrfVel (core,axis, &prfVel);
        if(CMD_SUCCESS != rtn)
        {
            return CommandHandler("GTN_GetPrfVel",rtn);
        }

        printf("sts=0x%-10lx,prfVel=%-10.2lf,prfPos=%-10.1lf\r", axisSts, prfVel, prfPos);
    }

    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*rtn = GTN_AxisOff(CORE,TRAP_MOTION_AXIS_NUMBER);
    if (0 != rtn)
    {
        return CommandHandler("GTN_AxisOff", rtn);
    }*/

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    printf("\nPress Any Key To Exit !\n");
    getchar();

    return 0;
}

1.2.4 PVT运动

1.2.4.1 指令列表

PVT运动模式指令列表

指令 说明
GTN_PrfPvt 设置指定轴为PVT运动模式。
GTN_SetPvtLoop 设置PVT运动模式循环次数。
GTN_GetPvtLoop 读取PVT运动模式循环次数。
GTN_PvtTable 向PVT运动模式指定数据表传送数据,采用PVT描述方式。
GTN_PvtTableEx 向PVT运动模式指定数据表传送数据,采用PVT描述方式。
GTN_PvtTableComplete 向PVT运动模式指定数据表传送数据,采用Complete描述方式。
GTN_PvtTablePercent 向PVT运动模式指定数据表传送数据,采用Percent描述方式。
GTN_PvtPercentCalculate 计算PVT运动模式Percent描述方式下各数据点的速度。
GTN_PvtTableContinuous 向PVT运动模式指定数据表传送数据,采用Continuous描述方式。
GTN_PvtContinuousCalculate 计算PVT运动模式Continuous描述方式下各数据点的时间。
GTN_PvtTableSelect 选择PVT运动模式数据表。
GTN_PvtStartPro 启动PVT运动。
GTN_PvtStatus 读取PVT运动状态。

1.2.4.2 重点说明

PVT模式是PT模式的进阶版本,其通过的位置、速度和时间这三种参数共同组成的数组来描述运动规律。与PT模式相同,PVT模式同样要求用户将待规划的速度曲线通过分段的形式来进行描述,其中位置、速度和时间满足如下函数关系:

如果给定相邻2个数据点的“位置、速度、时间”参数,可以得到如下方程组:

求解该方程组,可以得到a、b、c、d,因此相邻2个数据点的运动规律就可以确定下来。

1. 运动控制器提供32个数据表存储数据点。每个数据表具有1024个存储空间。数据表和轴之间相互独立,一个数据表可以供多个轴使用。

2. GTN_PvtTableGTN_PvtTableCompleteGTN_PvtTablePercentGTN_PvtTableContinuous指令向数据表中传递数据。这些指令会删除数据表中原先的数据,因此所有数据应当一次传送完毕。如果使用数据表的轴正在运动,禁止更新数据表。

3. 调用GTN_PvtTableSelect指令选择数据表。可以在运动状态下切换数据表,但是不会立即切换只有当前数据表执行完毕以后,才会切换到新的数据表。

4. 调用GTN_PvtStart启动运动。启动以后,各轴时间清0。如果第一个数据点的时间为0则立即启动,否则会延时启动,延时时间等于第一个数据点的时间。

5. 数据表可以循环执行,调用GTN_SetPvtLoop设置循环次数,循环次数为0表示无限循环。当遍历完数据表以后,时间初始化为第一个数据点的时间,而不是0。

PVT方式进行描述的循环运动数据示例

假设有如下所示的4个数据点,采用PVT方式进行描述,数据如下表格。

用PVT方式描述的数据点

数据点 时间(ms) 位置(pulse) 速度(pulse/ms)
P1 1000 0 0
P2 2000 5000 10
P3 3000 15000 10
P4 4000 20000 0
(1) 调用GTN_PrfPvt将轴切换到PVT模式。
(2) 调用GTN_PvtTable将4个数据点传递到数据表。
(3) 调用GTN_SetPvtLoop设置为循环执行。
(4) 调用GTN_PvtStart启动运动。
(5) 由于P1的时间为1000毫秒,因此调用GTN_PvtStart以后延时1000毫秒启动。由于是循环执行,到达P4以后返回到P1,速度曲线如图所示。
循环执行数据表

6. PVT模式有4种方式描述运动规律,PVT、Complete、Percent和Continuous,下面对此进行详细说明。

(1) PVT描述方式说明。

PVT描述方式直接定义各数据点的“位置、速度、时间”。相邻2个数据点之间,运动控制器使用3次多项式对位置进行插值,使用2次多项式对速度进行插值。因此当给出各数据点“位置、速度、时间”参数以后,相应的运动规律也就确定下来。

PVT描述方式数据点示例

如表所示的4组数据点,采用PVT描述方式。

PVT描述方式下的四组数据点
数据组 数据点 时间(ms) 位置(pulse) 速度(pulse/ms)
1 P1 1000 0 0
P2 1000 5000 10
P3 2000 15000 10
P4 3000 20000 0
2 P1 0 0 0
P2 1000 5000 9
P3 2000 15000 9
P4 3000 20000 0
3 P1 0 0 0
P2 1000 5000 7.5
P3 2333 15000 7.5
P4 3333 20000 0
4 P1 0 0 0
P2 750 1667 6.6669
P3 2250 18333 6.6669
P4 3000 20000 0
这4组数据点对应的运动规律如图所示。
合理的PVT描述方式运动规律
PVT描述方式非常灵活。给定数据点的“位置、速度、时间”参数,就能够得到相应的运动规律。需要注意的是,数据点参数需要仔细设计,否则难以得到理想的运动规律。例如下表所示的2组数据点参数不合理,得到的速度曲线不够平滑。
两组不合理的PVT描述方式下的数据点
数据组 数据点 时间(ms) 位置(pulse) 速度(pulse/ms)
1 P1 0 0 0
P2 1000 5000 15
P3 2000 15000 15
P4 3000 20000 0
2 P1 0 0 0
P2 1000 5000 5
P3 2000 15000 5
P4 3000 20000 0
这2组数据点对应的运动规律如图所示。
不合理的PVT描述方式运动规律

PVT描述方式程序示例

如图所示,整个速度曲线由5段组成,并且带有起跳速度。第一段速度增大,加速度保持不变;第二段速度增大,加速度减小;第三段速度不变,加速度为0;第四段速度减小,加速度增大;第五段速度减小,加速度不变。

PVT描述方式的运动规律例程
可以满足上述要求的一组数据表如以下所示。
PVT描述方式例程的数据点


数据点 时间(ms) 位置(pulse) 速度(pulse/ms)
P1 0 0 1
P2 1200 9750 15.25
P3 2000 24483 20
P4 3000 44483 20
P5 3800 59216 15.25
P6 5000 68966 1
// PVT_Pvt.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "math.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:Pvt运动示例
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)规划运动
//           (3)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                         // 指令返回值
    short core;                        // 需要执行例程的运动控制器核号
    short axis;                        // 需要初始化的轴起始索引号
    short axisCount;                   // 需要初始化的轴数量,从轴起始索引号开始算起
    double prfPos;                     // 实时读取的规划位置
    double prfVel;                     // 实时读取的规划速度
    long axisSts;                      // 轴状态

    double time[6]={0, 1200, 2000, 3000, 3800, 5000};
    double pos[6]={0, 9750, 24483, 44483, 59216, 68966};
    double vel[6]={1, 15.25, 20, 20, 15.25, 1};
    short dataCount=6;                    // PVT数据点数
    double timeTemp;
    short tableId=1;                    // PVT表号
    long mask;

    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 8;
    rtn = InitMc(core,axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*rtn = GTN_AxisOn(CORE,TRAP_MOTION_AXIS_NUMBER);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }*/

    // 设置为PVT模式
    rtn =GTN_PrfPvt(core,axis);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PrfPvt",rtn);
    };

    // 发送数据
    rtn =GTN_PvtTable(core,tableId,dataCount, &time[0], &pos[0], &vel[0]);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtTable",rtn);
    }

    // 选择数据表
    rtn =GTN_PvtTableSelect(core,axis,tableId);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtTableSelect",rtn);
    }

    // 启动PVT运动
    mask = 1<<(axis-1);
    rtn =GTN_PvtStart(core,mask);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtStart",rtn);
    }

    // 读取轴运动状态
    axisCount = 1;
    do 
    {
        rtn = GTN_GetSts(core,axis,&axisSts,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetSts",rtn);
        }

        // 读取数据表和运动时间
        rtn =GTN_PvtStatus(core,axis,&tableId,&timeTemp,axisCount);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_PvtStatus",rtn);
        }

        // 读取规划速度
        rtn =GTN_GetPrfVel(core,axis,&prfVel,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfVel",rtn);
        }

        // 读取规划位置
        rtn =GTN_GetPrfPos(core,axis,&prfPos,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfPos",rtn);
        }

        printf("数据表ID号=%2d 轴运动时间 = %10.0lf 轴运动速度 =%10.2lf 轴运动位置 = %10.1lf\r", tableId, timeTemp, prfVel, prfPos);

    } while (0x400 == (axisSts&0x400));

    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*rtn = GTN_AxisOff(CORE,TRAP_MOTION_AXIS_NUMBER);
    if (0 != rtn)
    {
        return CommandHandler("GTN_AxisOff", rtn);
    }*/

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    printf("\nPress Any Key To Exit !\n");
    getchar();

    return 0;
}

(2) Complete描述方式说明

Complete描述方式定义各数据点的“位置、时间”,以及起点速度和终点速度。Complete方式只定义了起点速度和终点速度。运动控制器根据各数据点的“位置、时间”参数计算中间各点的速度,确保各数据点速度连续和加速度连续。

Complete描述方式数据点示例

例如表中所示的这组数据点,采用Complete描述方式,可以得到光滑的速度曲线。

Complete描述方式的一组数据点

数据点 时间(ms) 位置(pulse) 速度(pulse/ms)
P1 0 0 1
P2 1000 5000 不指定
P3 2000 15000 不指定
P4 3000 20000 0
这组数据点对应的运动规律如图所示。
Complete描述方式运动规律
Complete适合描述光滑的速度曲线,例如三角函数等。假设位置和时间之间的关系由函数P=50000sin2 (π/2000t)确定。在一个函数周期[0, 2000]内取5个时间点计算相应的位置,如表所示:
Complete方式描述三角函数的数据点

数据点 时间(ms) 位置(pulse) 速度(pulse/ms)
P1 0 0 1
P2 500 25000 不指定
P3 1000 50000 不指定
P4 1500 25000 不指定
P5 2000 0 0
这组数据点对应的运动规律如图所示。
Complete方式描述三角函数运动规律
增加数据点可以减小与函数P=50000sin2 (π/2000
t)的逼近误差。下图给出了数据点数为5、10、50时的位置误差。
Complete方式下数据点数分别为5、10、50时的位置误差

Complete描述方式程序示例

假设位置和时间之间的关系由函数P=40000sin2 (π/2000*t)确定。要求启动以后能够循环运动,按A键幅值增大50%,Complete描述方式下的速度曲线如图所示。

Complete描述方式下的速度曲线
// PVT_Complete.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "math.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:Pvt Complete运动示例
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)规划运动
//           (3)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

#define PI 3.1415926

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

void Calculate(double amplitude,long n, double *pTime, double *pPos)
{
    long i;
    for(i=0;i<n;++i)
    {
        pPos[i] = amplitude*sin(PI/2000*pTime[i])*sin(PI/2000*pTime[i]);
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                         // 指令返回值
    short core;                        // 需要执行例程的运动控制器核号
    short axis;                        // 需要初始化的轴起始索引号
    short axisCount;                   // 需要初始化的轴数量,从轴起始索引号开始算起
    double prfPos;                     // 实时读取的规划位置
    double prfVel;                     // 实时读取的规划速度
    long axisSts;                      // 轴状态
    short tableId = 1;                  //tableID
    short loop_Count = 2;
    double time[5]={0, 500, 1000, 1500, 2000};
    double pos[5];
    double a[5], b[5], c[5];
    long mask;
    double amplitude = 40000;
    short dataCount=5;
    double velBegin = 0;
    double velEnd = 0;
    double timeTemp;
    unsigned long clock;

    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 8;
    rtn = InitMc(core,axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*rtn = GTN_AxisOn(CORE,TRAP_MOTION_AXIS_NUMBER);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }*/

    // 设置为PVT模式
    rtn =GTN_PrfPvt(core,axis);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PrfPvt",rtn);
    }

    // 将幅值为amplitude的sin曲线离散成dataCount个数据点
    Calculate(amplitude, dataCount, &time[0], &pos[0]);

    // 将sin曲线的数据作为PVTcomplete数据进行拟合,并下载到表tableId中
    rtn =GTN_PvtTableComplete(core,tableId, dataCount, &time[0], &pos[0], &a[0], &b[0], &c[0], velBegin, velEnd);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtTableComplete",rtn);
    }

    // 选择数据表
    rtn =GTN_PvtTableSelect(core,axis,tableId);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtTableSelect",rtn);
    }

    // 设置循环次数
    rtn =GTN_SetPvtLoop(core,axis,loop_Count);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_SetPvtLoop",rtn);
    }

    // 启动PVT运动
    mask = 1<<(axis-1);
    rtn =GTN_PvtStart(core,mask);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtStart",rtn);
    }

    // 读取轴运动状态
    axisCount = 1;
    do 
    {
        rtn = GTN_GetSts(core,axis,&axisSts,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetSts",rtn);
        }

        // 读取数据表和运动时间
        rtn =GTN_PvtStatus(core,axis,&tableId,&timeTemp,axisCount);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_PvtStatus",rtn);
        }

        // 读取规划速度
        rtn =GTN_GetPrfVel(core,axis,&prfVel,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfVel",rtn);
        }

        // 读取规划位置
        rtn =GTN_GetPrfPos(core,axis,&prfPos,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfPos",rtn);
        }

        printf("数据表ID号=%2d 轴运动时间 = %10.0lf 轴运动速度 =%10.2lf 轴运动位置 = %10.1lf\r", tableId, timeTemp, prfVel, prfPos);

    } while (0x400 == (axisSts&0x400));

    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*rtn = GTN_AxisOff(CORE,TRAP_MOTION_AXIS_NUMBER);
    if (0 != rtn)
    {
        return CommandHandler("GTN_AxisOff", rtn);
    }*/

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    printf("\nPress Any Key To Exit !\n");
    getchar();

    return 0;
}

(3) Percent描述方式

Percent描述方式定义各数据点的“位置、时间、百分比”,以及起点速度。Percent描述方式能够精确定义加速段、匀速段、减速段的位移、速度和时间。Percent描述方式假设相邻2个数据点之间速度为线性变化,利用起点速度以及各数据点的“位置、时间”参数,通过如下递推公式可以计算出各数据点的速度。

因此指定了各数据点的“位置、时间”参数以后,各数据点的速度实际上也就已经确定下来。通过“百分比”参数可以调整速度曲线的光滑性。数据点的百分比参数是指“相邻2个数据点之间加速度的变化时间占速度变化时间的百分比”。以下图为例来进行说明。

Percent描述方式下的百分比定义

数据点P1和P2之间加速度不变,因此数据点P1的百分比为0。数据点P2和P3之间加速度不变,因此数据点P2的百分比为0。数据点P3和P4之间加速度变化时间为2ta,运动时间为2ta+te,因此数据点P3的百分比为2ta/(2ta+te)*100%。

调整百分比参数,不会影响数据点的“位置、时间参数”。以上图为例,当数据点P3的百分比为0时,数据点P3和P4之间的速度曲线为虚线;当数据点P3的百分比不为0时,数据点P3和P4之间的速度曲线为实线。

Percent描述方式数据点示例

以下所示的这组数据点,采用Percent描述方式。

Percent描述方式下的数据点
数据点 时间(ms) 位置(pulse) 百分比 速度(pulse/ms)
P1 0 0 60 1
P2 1000 5000 0 不指定
P3 2000 15000 100 不指定
P4 3000 20000 0 不指定
这组数据点对应的运动规律如图所示。
Percent描述方式下的运动规律

Percent描述方式程序示例

X轴往复运动,Y轴正向进给。X轴加减速时Y轴开始进给,X轴匀速运动时,Y轴保持静止。X轴和Y轴的运动规律如图所示。

Percent描述方式下X轴和Y轴的运动规律
X轴取7个数据点,设置为循环模式。数据点参数见下表。
Percent描述方式下的数据点1

数据点 时间(ms) 位置(pulse) 百分比 速度(pulse/ms)
P1 0 0 60 0
P2 1000 5000 0 不指定
P3 2000 15000 60 不指定
P4 3000 20000 60 不指定
P5 4000 15000 0 不指定
P6 5000 5000 60 不指定
P7 6000 0 0 不指定
根据X轴数据点参数,可以计算出各数据点的速度,百分比参数对数据点的速度计算没有影响。
Percent描述方式下的数据点2

数据点 时间(ms) 位置(pulse) 速度(pulse/ms)
P1 0 0 0
P2 1000 5000 2(5000-0)/(1000-0)-0=10
P3 2000 15000 2(15000-5000)/(2000-1000)-10=10
P4 3000 20000 2(20000-15000)/(3000-2000)-10=0
P5 4000 15000 2(15000-20000)/(4000-3000)-0=-10
P6 5000 5000 2(5000-15000)/(5000-4000)-(-10)=-10
P7 6000 0 2(0-5000)/(6000-5000)-(-10)=0
Y轴取5个数据点,设置为循环模式。X轴启动以后到达数据点P3时Y轴才启动,因此第1个数据点的时间设置为2000毫秒。当Y轴到达P5以后,返回到P1循环执行。数据点参数如下表所示。
Percent描述方式下的数据点3
数据点 时间(ms) 位置(pulse) 百分比 速度(pulse/ms)
P1 2000 0 60 0
P2 2500 2500 0 不指定
P3 3500 12500 60 不指定
P4 4000 15000 0 不指定
P5 5000 15000 0 不指定
根据Y轴数据点参数,可以计算出各数据点的速度,百分比参数对数据点的速度计算没有影响。
Percent描述方式下的数据点4
数据点 时间(ms) 位置(pulse) 速度(pulse/ms)
P1 2000 0 0
P2 2500 2500 2(2500-0)/(2500-2000)-0=10
P3 3500 12500 2(12500-2500)/(3500-2500)-10=10
P4 4000 15000 2(15000-12500)/(4000-3500)-10=0
P5 5000 15000 2(15000-15000)/(5000-4000)-0=0
X轴循环n次,Y轴需要循环2n-1次。下图是当X轴的循环次数为2,Y轴循环次数为3时的XY位置图。横轴是X轴的位置,纵轴是Y轴的位置。实际的运动轨迹如图所示。
Percent描述方式下的X-Y位置图
// PvtPercent.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "math.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:Pvt Percent运动示例
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)规划运动
//           (3)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

#define PI 3.1415926

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                         // 指令返回值
    short core;                        // 需要执行例程的运动控制器核号
    short axis;                        // 需要初始化的轴起始索引号
    short axisCount;                   // 需要初始化的轴数量,从轴起始索引号开始算起
    long axisSts[2];                   // 轴状态
    short tableId[2];                  // 读取的tableID
    double timeTemp[2];                // 读取的运动时间
    short loop_Count = 2;              // pvt运动循环次数
    short dataCount;                   // pvt数据点数量
    unsigned long clock;               // 读取的控制器时钟
    double velBeigin=0;                // pvt起点速度
    double prfVel[2], prfPos[2];       // 读取的X轴、Y轴对应的规划速度和位置
    short table_X = 1;                 // X轴对应的pvt数据表
    short table_Y = 2;                 // Y轴对应的pvt数据表    
    short axisArray[2]={1,2};          // X轴、Y轴对应的轴号为1、2轴
    // X轴对应的pvt数据点参数
    double time_x[7] = {0, 1000, 2000, 3000, 4000, 5000, 6000};
    double pos_x[7] = {0, 5000, 15000, 20000, 15000, 5000, 0};
    double percent_x[7] = {60, 0, 60, 60, 0, 60, 0};
    // Y轴对应的pvt数据点参数
    double time_y[5] = {2000, 2500, 3500, 4000, 5000};
    double pos_y[5] = {0, 2500, 12500, 15000, 15000};
    double percent_y[5] = {60, 0, 60, 0, 0};

    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 8;
    rtn = InitMc(core,axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*
    rtn = GTN_AxisOn(core,axisArray[0]);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }

    rtn = GTN_AxisOn(core,axisArray[1]);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }
    */

    // X轴设置为PVT运动模式
    rtn =GTN_PrfPvt(core,axisArray[0]);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PrfPvt",rtn);
    }

    // Y轴设置为PVT运动模式
    rtn =GTN_PrfPvt(core,axisArray[1]);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PrfPvt",rtn);
    }

    // 向X轴的pvt数据表发送数据,共7个数据点
    dataCount = 7;
    rtn =GTN_PvtTablePercent(core,table_X, dataCount, &time_x[0], &pos_x[0], &percent_x[0],velBeigin);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtTablePercent",rtn);
    }

    // 向Y轴的pvt数据表发送数据,共7个数据点
    dataCount = 5;
    rtn =GTN_PvtTablePercent(core,table_Y, dataCount, &time_y[0], &pos_y[0], &percent_y[0],velBeigin);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtTablePercent",rtn);
    }

    // X轴选择数据表table_X
    rtn =GTN_PvtTableSelect(core,axisArray[0], table_X);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtTableSelect",rtn);
    }

    // Y轴选择数据表table_Y
    rtn =GTN_PvtTableSelect(core,axisArray[1], table_Y);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtTableSelect",rtn);
    }

    // 设置X轴的循环次数
    rtn =GTN_SetPvtLoop(core,axisArray[0], loop_Count);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_SetPvtLoop",rtn);
    }

    // 设置Y轴的循环次数
    loop_Count = 2*loop_Count - 1;
    rtn =GTN_SetPvtLoop(core,axisArray[1], loop_Count);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_SetPvtLoop",rtn);
    }

    // 同时启动X轴和Y轴
    axisCount = 2;
    rtn =GTN_PvtStartPro(core,&axisArray[0],axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtStartPro",rtn);
    }

    axisCount = 2;
    do 
    {
        // 读取X轴和Y轴,两个轴的状态
        rtn = GTN_GetSts(core,axis,&axisSts[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetSts",rtn);
        }

        // 读取X轴和Y轴,两个轴的关联的数据表和运动时间
        rtn =GTN_PvtStatus(core,axis,&tableId[0],&timeTemp[0],axisCount);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_PvtStatus",rtn);
        }

        // 读取X轴和Y轴,两个轴的规划速度
        rtn =GTN_GetPrfVel(core,axis,&prfVel[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfVel",rtn);
        }

        // 读取X轴和Y轴,两个轴的规划位置
        rtn =GTN_GetPrfPos(core,axis,&prfPos[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfPos",rtn);
        }

        printf("x轴表ID号:%2d 运动时间:%6.0lf 速度:%6.2lf 位置:%6.1lf y轴表ID号:%2d 运动时间:%6.0lf 速度:%6.2lf 位置:%6.1lf\r", 
            tableId[0], timeTemp[0], prfVel[0], prfPos[0], tableId[1], timeTemp[1], prfVel[1], prfPos[1]);

    } while ( (0x400 == (axisSts[0]&0x400)) || (0x400 == (axisSts[1]&0x400)));

    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*
    rtn = GTN_AxisOff(core,axisArray[0]);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }

    rtn = GTN_AxisOff(core,axisArray[1]);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }
    */

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    printf("\nPress Any Key To Exit !\n");
    getchar();

    return 0;
}

(4) Continuous描述方式

Continuous描述方式定义各数据点的“位置、速度、最大速度、加速度、减速度、百分比”。不用指定数据点的时间。运动控制器根据数据点参数,自动将相邻2个数据点之间拆分为加速段、匀速段和减速段。

数据点Pi的最大速度是指从数据点Pi到数据点Pi+1之间的速度上限。数据点Pi的加速度是指从数据点Pi到数据点Pi+1之间的加速段所使用的加速度。数据点Pi的减速度是指从数据点Pi到数据点Pi+1之间的减速段所使用的减速度。数据点Pi的百分比是指从数据点Pi到数据点Pi+1之间的加减速段中,加速度变化时间占速度变化时间的百分比。

相邻2个数据点之间能够拆分出来的段数和这2个数据点的参数有关,图示例了一些可能的分段情况。

Continuous描述方式
Continuous描述方式数据点示例

下表所示的这两组数据点,采用Continuous描述方式。

Continuous描述方式下的数据点
数据组 数据点 位置 速度 最大速度 加速度 减速度 百分比
1 P1 0 0 10 0.01 0.01 60
P2 20000 0 10 0.01 0.01 0
2 P1 0 0 10 0.01 0.01 60
P2 19800 2 2 0.02 0.02 0
P3 21800 0 2 0.02 0.02 0

Continuous描述方式程序示例

X轴从A点运动到B点,Y轴从C点运动到D点。要求X轴到达B点时,Y轴同时到达D点。 首先调用GTN_PvtContinuousCalculate指令计算X轴的运动时间tx和Y轴的运动时间ty。该指令不会把数据点发送到运动控制器。如果tx>ty,Y轴延时tx-ty启动;如果tx<ty,X轴延时ty-tx启动,X轴和Y轴速度曲线如图所示。

Continuous描述方式下的X轴和Y轴速度曲线
// pvtContinuous.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "math.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:Pvt Continuous运动示例
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)规划运动
//           (3)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

#define PI 3.1415926

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                         // 指令返回值
    short core;                        // 需要执行例程的运动控制器核号
    short axis;                        // 需要初始化的轴起始索引号
    short axisCount;                   // 需要初始化的轴数量,从轴起始索引号开始算起
    long axisSts[2];                   // 轴状态
    short tableId[2];                  // 读取的tableID
    double timeTemp[2];                // 读取的运动时间
    short dataCount;                   // pvt数据点数量
    unsigned long clock;               // 读取的控制器时钟
    double prfVel[2], prfPos[2];       // 读取的X轴、Y轴对应的规划速度和位置
    short table_X = 1;                 // X轴对应的pvt数据表
    short table_Y = 2;                 // Y轴对应的pvt数据表    
    short axisArray[2]={1,2};          // X轴、Y轴对应的轴号为1、2轴
    // X轴对应的pvt数据点参数
    double pos_x[2] = {0, 30000};
    double vel_x[2] = {0, 0};
    double percent_x[2] = {100, 100};
    double velMax_x[2] = {10, 10};
    double acc_x[2] = {0.01, 0.01};
    double dec_x[2] = {0.01, 0.01};
    double time_x[2];
    double timeBegin_x;
    // Y轴对应的pvt数据点参数
    double pos_y[2] = {0, 20000};
    double vel_y[2] = {0, 0};
    double percent_y[2] = {100, 100};
    double velMax_y[2] = {10, 10};
    double acc_y[2] = {0.01, 0.01};
    double dec_y[2] = {0.01, 0.01};
    double time_y[2];
    double timeBegin_y;

    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 8;
    rtn = InitMc(core,axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*
    rtn = GTN_AxisOn(core,axisArray[0]);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }

    rtn = GTN_AxisOn(core,axisArray[1]);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }
    */

    // X轴设置为PVT运动模式
    rtn =GTN_PrfPvt(core,axisArray[0]);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PrfPvt",rtn);
    }

    // Y轴设置为PVT运动模式
    rtn =GTN_PrfPvt(core,axisArray[1]);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PrfPvt",rtn);
    }

    //计算X轴运动时间,共2个数据点
    dataCount = 2;
    rtn = GTN_PvtContinuousCalculate(core,dataCount,&pos_x[0],&vel_x[0],&percent_x[0],
        &velMax_x[0],&acc_x[0],&dec_x[0],&time_x[0]);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtContinuousCalculate",rtn);
    }

    //计算Y轴运动时间,共2个数据点
    rtn = GTN_PvtContinuousCalculate(core,dataCount,&pos_y[0],&vel_y[0],&percent_y[0],
        &velMax_y[0],&acc_y[0],&dec_y[0],&time_y[0]);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtContinuousCalculate",rtn);
    }

    // 计算启动延时,达到同时停止的效果
    if( time_x[1] <time_y[1] )
    {
        timeBegin_x = time_y[1] - time_x[1];
        timeBegin_y = 0;
    }
    else
    {
        timeBegin_x = 0;
        timeBegin_y = time_x[1] - time_y[1];
    }

    // 向X轴的pvt数据表发送数据,共2个数据点
    rtn =GTN_PvtTableContinuous(core,table_X, dataCount, &pos_x[0], &vel_x[0], &percent_x[0],
        &velMax_x[0],&acc_x[0],&dec_x[0],timeBegin_x);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtTableContinuous",rtn);
    }

    // 向Y轴的pvt数据表发送数据,共2个数据点
    rtn =GTN_PvtTableContinuous(core,table_Y, dataCount, &pos_y[0], &vel_y[0], &percent_y[0],
        &velMax_y[0],&acc_y[0],&dec_y[0],timeBegin_y);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtTableContinuous",rtn);
    }

    // X轴选择数据表table_X
    rtn =GTN_PvtTableSelect(core,axisArray[0], table_X);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtTableSelect",rtn);
    }

    // Y轴选择数据表table_Y
    rtn =GTN_PvtTableSelect(core,axisArray[1], table_Y);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtTableSelect",rtn);
    }

    // 同时启动X轴和Y轴
    axisCount = 2;
    rtn =GTN_PvtStartPro(core,&axisArray[0],axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PvtStartPro",rtn);
    }

    axisCount = 2;
    do 
    {
        // 读取X轴和Y轴,两个轴的状态
        rtn = GTN_GetSts(core,axis,&axisSts[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetSts",rtn);
        }

        // 读取X轴和Y轴,两个轴的关联的数据表和运动时间
        rtn =GTN_PvtStatus(core,axis,&tableId[0],&timeTemp[0],axisCount);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_PvtStatus",rtn);
        }

        // 读取X轴和Y轴,两个轴的规划速度
        rtn =GTN_GetPrfVel(core,axis,&prfVel[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfVel",rtn);
        }

        // 读取X轴和Y轴,两个轴的规划位置
        rtn =GTN_GetPrfPos(core,axis,&prfPos[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfPos",rtn);
        }

        printf("x轴表ID号:%2d 运动时间:%6.0lf 速度:%6.2lf 位置:%6.1lf y轴表ID号:%2d 运动时间:%6.0lf 速度:%6.2lf 位置:%6.1lf\r", 
            tableId[0], timeTemp[0], prfVel[0], prfPos[0], tableId[1], timeTemp[1], prfVel[1], prfPos[1]);

    } while ( (0x400 == (axisSts[0]&0x400)) || (0x400 == (axisSts[1]&0x400)));

    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*
    rtn = GTN_AxisOff(core,axisArray[0]);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }

    rtn = GTN_AxisOff(core,axisArray[1]);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }
    */

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    printf("\nPress Any Key To Exit !\n");
    getchar();

    return 0;
}

1.3 单轴连续运动模式

1.3.1 Jog运动

1.3.1.1 指令列表

Jog运动模式指令列表

指令 说明
GTN_PrfJog 设置指定轴为Jog运动模式。
GTN_SetJogPrm 设置Jog运动模式下的运动参数。
GTN_GetJogPrm 读取Jog运动模式下的运动参数。
GTN_SetVel 设置目标速度。
GTN_GetVel 读取目标速度。
GTN_UpdatePro 启动运动。

1.3.1.2 重点说明

在Jog运动模式下,各轴可以独立设置目标速度、加速度、减速度、平滑系数等运动参数,能够独立运动或停止。

1. 如何切换到Jog运动?

用户必须要调GTN_PrfJog,才能将指定轴设定为Jog模式。

2. 如何设置Jog运动参数?

用户调用GTN_SetJogPrm设置Jog运动的运动参数,调用GTN_SetVel设置Jog运动的目标速度。

3. 如何启动Jog运动?

用户调用GTN_UpdatePro指令启动Jog运动,之后控制器根据设定的运动参数自动生成相应的梯形曲线速度规划,并且在运动过程中可以随时修改目标速度。如图所示。

Jog模式速度曲线

设定平滑系数能够得到平滑的速度曲线,从而使加减速过程更加平稳。平滑系数的取值范围是[0, 1),越接近1,加速度变化越平稳。

Jog运动
// jog.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "math.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:Jog运动示例
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)规划运动
//           (3)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

#define PI 3.1415926

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                          // 指令返回值
    short core;                         // 需要执行例程的运动控制器核号
    short axis;                         // 需要初始化的轴起始索引号
    short axisCount;                    // 需要初始化的轴数量,从轴起始索引号开始算起
    long axisSts;                       // 轴状态
    unsigned long clock;                // 读取的控制器时钟
    double prfVel,prfPos;               // 读取的规划速度和位置
    double prfPosStart;                 // 运动前的规划位置
    TJogPrm jogPrm;                     // Jog运动参数
    double targetVel;                   // 目标速度
    short updateVelFlag=1;              // 更新速度的标致
    short stopFlag=1;                   // 更新速度的标致
    long mask,stopOption;


    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 1;
    rtn = InitMc(core,axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*
    rtn = GTN_AxisOn(core,axis);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }
    */

    // 将该轴设置为Jog运动模式
    rtn = GTN_PrfJog(core,axis);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PrfJog",rtn);
    }

    // 读取Jog运动参数(需要读取全部运动参数到上位机变量)
    memset(&jogPrm,0,sizeof(jogPrm));
    rtn = GTN_GetJogPrm(core,axis,&jogPrm);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_GetJogPrm",rtn);
    }

    // 设置需要修改的运动参数
    jogPrm.acc = 0.0625;
    jogPrm.dec = 0.0625;

    // 设置Jog运动参数
    rtn = GTN_SetJogPrm(core,axis,&jogPrm);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_SetJogPrm",rtn);
    }

    // 设置该轴的目标速度
    targetVel = 20;
    rtn = GTN_SetVel(core,axis,targetVel);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_SetVel",rtn);
    }

    // 启动该轴的运动
    rtn = GTN_UpdatePro(core,&axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_UpdatePro",rtn);
    }

    rtn =GTN_GetPrfPos(core,axis,&prfPosStart,axisCount,&clock);
    do 
    {
        // 读取该轴的轴状态
        rtn = GTN_GetSts(core,axis,&axisSts,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetSts",rtn);
        }

        // 读取该轴的规划速度
        rtn =GTN_GetPrfVel(core,axis,&prfVel,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfVel",rtn);
        }

        // 读取该轴的规划位置
        rtn =GTN_GetPrfPos(core,axis,&prfPos,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfPos",rtn);
        }

        printf("sts=0x%-10lxprfPos=%-10.1lfprfVel=%-10.1lf\r", axisSts, prfPos, prfVel);

        // 规划位置运动10000的增量后修改成新的目标速度
        if((prfPos >= (prfPosStart + 10000)) && (1 == updateVelFlag))
        {
            updateVelFlag = 0;
            // 设置新的目标速度
            targetVel = 10;
            rtn = GTN_SetVel(core,axis,targetVel);
            if ( CMD_SUCCESS != rtn )
            {
                return CommandHandler("GTN_SetVel",rtn);
            }

            // 使新的目标速度生效
            rtn = GTN_UpdatePro(core,&axis,axisCount);
            if ( CMD_SUCCESS != rtn )
            {
                return CommandHandler("GTN_UpdatePro",rtn);
            }
        }

        // 规划位置运动20000的增量后停止运动stopFlag
        if((prfPos >= (prfPosStart + 20000)) && (1 == stopFlag))
        {
            stopFlag = 0;
            mask = 1 << (axis-1);
            stopOption = 0;         // 平滑停止
            rtn = GTN_Stop(core,mask,stopOption);
            if ( CMD_SUCCESS != rtn )
            {
                return CommandHandler("GTN_Stop",rtn);
            }
        }

    } while ( 0x400 == (axisSts&0x400));

    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*
    rtn = GTN_AxisOff(core,axis);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }
    */

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    printf("\nPress Any Key To Exit !\n");
    getchar();

    return 0;
}

1.3.2 MoveVelocity运动

1.3.2.1 指令列表

MoveVelocity运动模式指令列表

指令 说明
GTN_MoveVelocity 设置MoveVelocity运动参数并启动。
GTN_GetMoveVelocity 读取MoveVelocity运动参数。

1.3.2.2 重点说明

MoveVelocity运动从当前速度按照设定的加速度和加加速度运动到目标速度,到达目标速度以后一直保持目标速度。

1. 如何切换至MoveVelocity模式?

用户调用GTN_MoveVelocity指令后,将指定轴从其他运动模式(除插补运动)立即切换到MoveVelocity运动,该切换可无需等待其他运动停止。

2. 如何设置并启动MoveVelocity运动?

MoveVelocity运动通过GTN_MoveVelocity指令设置运动参数,并通过该指令立即启动MoveVelocity运动。

MoveVelocity运动可以设置不同的加速度和减速度,实现非对称加减速。当加速时使用acc,减速时使用dec。direction设置运动方向,如图所示:

MC_POSITIVE_DIRECTION(0):正向运动。

MC_NEGATIVE_DIRECTION(1):负向运动。

MC_CURRENT_DIRECTION(2):保持当前运动方向。

jerkBegin设置加速度从0变化到最大加速度的加加速度。

jerkEnd设置加速度从最大加速度变化到0的加加速度。

通过调整jerkBegin和jerkEnd,可以实现非对称S曲线。

当jerkBegin大于jerkEnd时的速度曲线如图所示。

MoveVelocity模式速度曲线
MoveVelocity运动
// MoveVelocity.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "math.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:MoveVelocity运动示例
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)规划运动
//           (3)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

#define PI 3.1415926

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                          // 指令返回值
    short core;                         // 需要执行例程的运动控制器核号
    short axis;                         // 需要初始化的轴起始索引号
    short axisCount;                    // 需要初始化的轴数量,从轴起始索引号开始算起
    long axisSts;                       // 轴状态
    unsigned long clock;                // 读取的控制器时钟
    double prfVel,prfPos;               // 读取的规划速度和位置
    double prfPosStart;                 // 运动前的规划位置
    TJogPrm jogPrm;                     // Jog运动参数
    double targetVel;                   // 目标速度
    TMoveVelocityPrm moveVelocityPrm;   // MoveVelocity运动参数
    short updateVelFlag=1;              // 更新速度的标致
    short stopFlag=1;                   // 更新速度的标致
    long mask,stopOption;


    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 1;
    rtn = InitMc(core,axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*
    rtn = GTN_AxisOn(core,axis);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }
    */

    // 将该轴设置为Jog运动模式
    rtn = GTN_PrfJog(core,axis);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PrfJog",rtn);
    }

    // 读取Jog运动参数(需要读取全部运动参数到上位机变量)
    memset(&jogPrm,0,sizeof(jogPrm));
    rtn = GTN_GetJogPrm(core,axis,&jogPrm);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_GetJogPrm",rtn);
    }

    // 设置需要修改的运动参数
    jogPrm.acc = 0.0625;
    jogPrm.dec = 0.0625;

    // 设置Jog运动参数
    rtn = GTN_SetJogPrm(core,axis,&jogPrm);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_SetJogPrm",rtn);
    }

    // 设置该轴的目标速度
    targetVel = 20;
    rtn = GTN_SetVel(core,axis,targetVel);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_SetVel",rtn);
    }

    // 启动该轴的运动
    rtn = GTN_UpdatePro(core,&axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_UpdatePro",rtn);
    }

    rtn =GTN_GetPrfPos(core,axis,&prfPosStart,axisCount,&clock);
    do 
    {
        // 读取该轴的轴状态
        rtn = GTN_GetSts(core,axis,&axisSts,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetSts",rtn);
        }

        // 读取该轴的规划速度
        rtn =GTN_GetPrfVel(core,axis,&prfVel,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfVel",rtn);
        }

        // 读取该轴的规划位置
        rtn =GTN_GetPrfPos(core,axis,&prfPos,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfPos",rtn);
        }

        printf("sts=0x%-10lx,prfPos=%-10.1lf,prfVel=%-10.1lf\r", axisSts, prfPos, prfVel);

        // 规划位置运动10000的增量后修改成新的目标速度
        if((prfPos >= (prfPosStart + 10000)) && (1 == updateVelFlag))
        {
            updateVelFlag = 0;
            // 设置新的目标速度
            targetVel = 10;
            memset(&moveVelocityPrm,sizeof(moveVelocityPrm),0);
            moveVelocityPrm.acc = 0.5;
            moveVelocityPrm.dec = 0.25;
            moveVelocityPrm.vel = 60;
            moveVelocityPrm.jerkBegin = 5;      // 从当前加速度变化到最大加速度acc的加加速度
            moveVelocityPrm.direction = 0;

            rtn = GTN_MoveVelocity(core,axis,&moveVelocityPrm);
        }

        // 规划位置运动40000的增量后停止运动stopFlag
        if((prfPos >= (prfPosStart + 40000)) && (1 == stopFlag))
        {
            stopFlag = 0;
            mask = 1 << (axis-1);
            stopOption = 0;         // 平滑停止
            rtn = GTN_Stop(core,mask,stopOption);
            if ( CMD_SUCCESS != rtn )
            {
                return CommandHandler("GTN_Stop",rtn);
            }
        }

    } while ( 0x400 == (axisSts&0x400));

    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*
    rtn = GTN_AxisOff(core,axis);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }
    */

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    printf("\nPress Any Key To Exit !\n");
    getchar();

    return 0;
}

1.4 单轴手轮运动模式

1.4.1 指令列表

单轴手轮功能指令列表

指令 说明
GTN_HandwheelInit 初始化单轴手轮功能。
GTN_StartHandwheel 启动单轴手轮。
GTN_EndHandwheel 结束单轴手轮功能。

1.4.2 重点说明

将手轮连接网络型模块的MPG硬件接口,再启动单轴手轮功能,单轴手轮能够实现以下功能:

1. 轴在静止状态下可以进入和退出手轮模式及设置手轮参数。

2. 手轮单格摇动时控制从轴可以准确运动设定的距离。

3. 手轮快速摇动时,从轴可以平稳运动。

4. 手轮不摇时,从轴能够根据设置的参数快速停止。

单轴手轮
// handWheel.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "math.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:单轴手轮引导功能,
// 测试平台:网络型运动控制器,等环网从站403模块、电机与手轮,
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)将轴1设置为从轴,MPG作为主轴的源,从轴会随着手轮转动而转动
//           (3)关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

#define PI 3.1415926

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                          // 指令返回值
    short core;                         // 需要执行例程的运动控制器核号
    short axis;                         // 需要初始化的轴起始索引号
    short axisCount;                    // 需要初始化的轴数量,从轴起始索引号开始算起
    long axisSts;                       // 轴状态
    unsigned long clock;                // 读取的控制器时钟
    double encVel,encPos;               // 读取的编码器速度和位置
    long mask,stopOption;

    short master;                       // 主轴索引
    short slave;                        // 从轴索引
    short masterEven;                   // 主轴传动比系数,主轴的位移,
    short slaveEven;                    // 从轴传动比系数,从轴的位移,(两个系数的比值就是齿轮比)。
    short intervalTime;                 // 主轴位移采样时间间隔,取值范围:[1, 32767],单位:规划周期。
    double acc;                         // 跟随过程中从轴的加速度,正值
    double dec;                         // 跟随过程中从轴的减速度,正值。
    double vel;                         // 跟随过程中从轴的最大速度,正值。
    short stopWaitTime;                 // 判断停止时间,取值范围:[1, 32767],单位:规划周期。
                                        // 高速跟随时,主轴急停,从轴经过多少时间后才停止,防止从轴跟随冲击过大。

    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 1;
    rtn = InitMc(core,axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*
    rtn = GTN_AxisOn(core,axis);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }
    */

    //使用MPG作为主轴的源,初始化单轴手轮功能
    rtn = GTN_HandwheelInit(core,MC_MPG);  
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_HandwheelInit",rtn);
    }

     // 启动单轴手轮
    master = 1;                             // 使用第1路MPG作为主轴
    slave = axis;                           // 1轴为从轴
    masterEven = 1;                         // 主、从轴传动比为1:100
    slaveEven = 100;
    intervalTime = 1;                       // 主轴位移采样时间间隔为100个控制器周期,
    acc = 0.5;
    dec = 0.5;
    vel = 100;
    stopWaitTime = 200;                     // 手轮停止时间超过200个控制器周期,被判定为停止。
    rtn = GTN_StartHandwheel(core,slave,master,masterEven,slaveEven,intervalTime,acc,dec,vel,stopWaitTime);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_StartHandwheel",rtn);
    }
/*
    do 
    {
        // 读取从轴的轴状态
        rtn = GTN_GetSts(core,axis,&axisSts,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetSts",rtn);
        }

        // 读取从轴的编码器速度
        rtn = GTN_GetEncVel(core,axis,&encVel,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetEncVel",rtn);
        }

        // 读取从轴的编码器位置
        rtn =GTN_GetEncPos(core,axis,&encPos,axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfPos",rtn);
        }

        printf("从轴状态sts=0x%-10lx从轴encPos=%-10.1lf从轴encVel=%-10.1lf\r", axisSts, encPos, encVel);

    } while ( 0x400 == (axisSts&0x400));
*/

    mask = 1 << (axis-1);
    stopOption = 0;         // 平滑停止
    rtn = GTN_Stop(core,mask,stopOption);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_Stop",rtn);
    }

    //退出单轴手轮
    rtn = GTN_EndHandwheel(core,slave);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_EndHandwheel",rtn);
    }
    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*
    rtn = GTN_AxisOff(core,axis);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }
    */

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    printf("\nPress Any Key To Exit !\n");
    getchar();

    return 0;
}

1.5 同步运动模式

1.5.1 电子齿轮运动

1.5.1.1 指令列表

电子齿轮运动模式指令列表

指令 说明
GTN_PrfGear 设置指定轴为电子齿轮运动模式。
GTN_SetGearMaster 设置电子齿轮运动跟随主轴。
GTN_GetGearMaster 读取电子齿轮运动跟随主轴。
GTN_SetGearRatio 设置电子齿轮比。
GTN_GetGearRatio 读取电子齿轮比。
GTN_GearStart 启动电子齿轮运动。

1.5.1.2 重点说明

电子齿轮模式能够将两轴或多轴联系起来,实现精确的同步运动,从而替代传统的机械齿轮连接。

我们把被跟随的轴叫主轴,把跟随的轴叫从轴。电子齿轮模式下,1个主轴能够驱动多个从轴,从轴可以跟随主轴的规划位置、编码器位置。

传动比:主轴速度与从轴速度的比例。电子齿轮模式能够灵活的设置传动比,节省机械系统的安装时间。当主轴速度变化时,从轴会根据设定好的传动比自动改变速度。电子齿轮模式也能够在运动过程中修改传动比。

离合区:当改变传动比时,可以设置离合区,实现平滑变速,如图所示,阴影区域为离合区。电子齿轮模式速度曲线如图所示。

注意

离合区位移是指从轴平滑变速过程中主轴运动的位移。不要计算成从轴变速时走过的位移。

电子齿轮模式速度曲线

主轴匀速运动,从轴为电子齿轮模式。在离合区1从轴速度从0逐渐增大,直到到达传动比4:3。当改变传动比至2:1时,在离合区2从轴速度逐渐变化直到满足新的传动比。离合区越大,从轴传动比的变化过程越平稳。当主轴速度变化时,从轴速度也随着变化,保持固定的传动比。

1. 如何切换到电子齿轮模式?

用户必须要调用GTN_PrfGear才能将指定轴设定为Gear模式。应将从轴设定为Gear模式。

2. 如何设置主轴?

调用GTN_SetGearMaster。参数profile为从轴轴号,参数masterIndex为主轴轴号。

重要
  1. 为了减少跟随滞后,从轴的轴号应当大于主轴的轴号。
  2. 默认情况下,主轴和跟随轴必须在同一核。

3. 如何设定传动比和离合区?

(1) 调用GTN_SetGearRatio指令来设置传动比和离合区。profile是从轴轴号;masterEven是主轴位移,slaveEven是从轴位移,masterEven/slaveEven等于传动比;slope是主轴离合区位移,即主轴在离合区内走过的位移,用户应自行计算出来。

(2) 如果从轴轴号为slave,当主轴位移alpha时从轴位移beta,主轴运动slope位移后从轴到达设定传动比,应当调用指令GTN_SetGearRatio

电子齿轮跟随

主轴为Jog模式,从轴为电子齿轮模式,传动比为主轴速度:从轴速度=2:1,主轴运动离合区位移后(图中阴影部分的区域),从轴达到设定的传动比。

电子齿轮模式主轴速度规划
电子齿轮模式从轴速度规划
// gear.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "conio.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:Gear电子齿轮运动测试功能,
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)将主轴切换为jog运动模式,设置主轴运动参数,将从轴设置为电子齿轮跟随运动模式,设置从轴的速度以及运动脉冲
//           (3)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                          // 指令返回值
    short core;                         // 需要执行例程的运动控制器核号
    short axis;                         // 需要初始化的轴起始索引号
    short axisCount;                    // 需要初始化的轴数量,从轴起始索引号开始算起
    long axisSts[2];                    // 轴状态
    unsigned long clock;                // 读取的控制器时钟
    double prfVel[2],prfPos[2];         // 读取的规划速度和位置
    TJogPrm jogPrm;                     // Jog运动参数
    double targetVel;                   // 目标速度
    short updateVelFlag=1;              // 更新速度的标致
    short stopFlag=1;                   // 更新速度的标致
    long mask,stopOption;

    short masterAxis = 1;               // 主轴索引
    short slaveAxis = 2;                // 从轴索引
    long masterEven = 2;                // 主轴传动比系数,
    long slaveEven = 1;                 // 从轴传动比系数,
    long masterSlope = 100000;          // 主轴离合区位移
    short masterType;                   // 主轴类型。
    short masterItem=0;                 // 输出位置类型,当masterType=GEAR_MASTER_AXIS(3)时起作用。

    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 2;
    rtn = InitMc(core,axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开

    /*
    rtn = GTN_AxisOn(core,masterAxis);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn",rtn);
    }

    rtn = GTN_AxisOn(core,slaveAxis);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn",rtn);
    }
    */

    // 将主轴设置为Jog运动模式
    rtn = GTN_PrfJog(core,masterAxis);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PrfJog",rtn);
    }

    // 读取Jog运动参数(需要读取全部运动参数到上位机变量)
    memset(&jogPrm,0,sizeof(jogPrm));
    rtn = GTN_GetJogPrm(core,masterAxis,&jogPrm);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_GetJogPrm",rtn);
    }

    // 设置需要修改的运动参数
    jogPrm.acc = 1;
    jogPrm.dec = 1;
    // 设置Jog运动参数
    rtn = GTN_SetJogPrm(core,masterAxis,&jogPrm);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_SetJogPrm",rtn);
    }

    // 设置主轴的目标速度
    targetVel = 100;
    rtn = GTN_SetVel(core,masterAxis,targetVel);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_SetVel",rtn);
    }

    // 只启动主轴的运动
    axisCount = 1;
    rtn = GTN_UpdatePro(core,&masterAxis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_UpdatePro",rtn);
    }

    // 将从轴设为Gear模式
    rtn = GTN_PrfGear(core,slaveAxis);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_PrfGear",rtn);
    }

    // 设置主轴,跟随主轴的规划位置
    masterType = GEAR_MASTER_PROFILE;
    rtn = GTN_SetGearMaster(core,slaveAxis, masterAxis,masterType,masterItem);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_SetGearMaster",rtn);
    }

    // 设置从轴的传动比和离合区,主轴运动位置的增量为masterSlope后,主轴、从轴的速度比例为masterEven / slaveEven
    rtn = GTN_SetGearRatio(core,slaveAxis, masterEven, slaveEven, masterSlope);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_SetGearRatio",rtn);
    }

    // 启动从轴
    rtn = GTN_GearStart(core,1<<(slaveAxis-1));
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_GearStart",rtn);
    }

    printf("\nPress Any Key To Exit !\n");
    axisCount = 2;
    while(!kbhit())
    {
        // 读取该轴的轴状态
        rtn = GTN_GetSts(core,axis,&axisSts[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetSts",rtn);
        }

        // 读取该轴的规划速度
        rtn =GTN_GetPrfVel(core,axis,&prfVel[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfVel",rtn);
        }

        // 读取该轴的规划位置
        rtn =GTN_GetPrfPos(core,axis,&prfPos[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfPos",rtn);
        }

        printf("主轴prfPos=%-10.1lf,主轴prfVel=%-10.1lf,从轴prfPos=%-10.1lf,从轴prfVel=%-10.1lf\r", 
            prfPos[0], prfVel[0], prfPos[1], prfVel[1]);
    } 

    // 平滑停止
    mask = 1 << (masterAxis - 1);
    mask |= 1 << (slaveAxis - 1);
    stopOption = 0;         
    rtn = GTN_Stop(core,mask,stopOption);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_Stop",rtn);
    }

    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*
    rtn = GTN_AxisOff(core,slaveAxis);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }

    rtn = GTN_AxisOff(core,masterAxis);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }
    */

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    return 0;
}

1.5.2 FollowEx运动

1.5.2.1 指令列表

FollowEx运动模式指令列表

指令 说明
GTN_PrfFollowEx 设置指定轴为FollowEx运动。
GTN_SetFollowMasterEx 设置FollowEx运动模式下的跟随主轴。
GTN_GetFollowMasterEx 读取FollowEx运动模式下的跟随主轴。
GTN_SetFollowLoopEx 设置FollowEx运动模式下的循环次数。
GTN_GetFollowLoopEx 读取FollowEx运动模式循环已经执行完成的次数。
GTN_SetFollowEventEx 设置FollowEx运动模式启动跟随条件。
GTN_GetFollowEventEx 读取FollowEx运动模式启动跟随条件。
GTN_FollowSpaceEx 查询FollowEx运动模式指定FIFO的剩余空间。
GTN_FollowDataPercent2Ex 向FollowEx运动模式指定FIFO增加数据,支持非对称S曲线。
GTN_GetFollowDataPercent2Ex 计算非对称S曲线的从轴段长。
GTN_FollowClearEx 清除FollowEx运动模式指定FIFO中的数据。
GTN_FollowStartEx 启动FollowEx运动。
GTN_FollowSwitchNowEx FollowEx模式下立即切换运动段或者运动FIFO。
GTN_SetFollowMemoryEx 设置FollowEx运动模式的缓存区大小。
GTN_GetFollowMemoryEx 读取FollowEx运动模式的缓存区大小。
GTN_FollowDoBitEx 向FollowEx运动模式指定FIFO增加操作DO的数据。
GTN_FollowDelayEx 向FollowEx运动模式指定FIFO增加操作延迟的数据。
GTN_FollowDiBitEx 向FollowEx运动模式指定FIFO增加操作等待DI的数据。

1.5.2.2 重点说明

在很多应用中,两轴或多轴之间需要保证位置同步和速度同步。我们把被跟随的轴叫主轴,把跟随的轴叫从轴。一对典型的主轴和从轴的规划如图所示。

Follow模式主从轴规划

图中画了一个周期的Follow运动,其中横坐标x表示主轴的位移,纵轴y表示从轴速度Vs与主轴速度Vm的比率。根据Sm*(Vs/Vm)可得:Vs*(Sm/Vm)=Vs*t,即图中FollowEx模式主从轴规划中的面积为从轴的位移。整个跟随模式如下:

1. 在主轴运动到设定位置(1000pulse)时,从轴启动跟随。

2. 在主轴运动到1500pulse时,从轴运动250 ( (1500-1000)*1/2 )pulse到达速度同步区,即图中阴影部分所示的速度同步区。

3. 在主轴运动到2250pulse时,从轴与主轴的分别同时到达各自的位置点,即图中标注的位置同步点。

(1) 位置同步点表示主轴和从轴必须同时到达各自指定位置。

(2) 速度同步区表示主轴和从轴之间必须保持准确的速度比。

Follow模式就是针对这种应用,给用户提供了主轴和从轴的位置和速度规划方式。用户只需要学习如何设置主轴,从轴,从轴启动跟随的条件,如何设置FollowEx循环次数,如何利用FollowEx模式中的数据段类型实现应用中所需要的规划以及如何管理FIFO,就可以轻松实现FollowEx运动。

1. 如何切换到FollowEx模式?

用户必须要调GTN_PrfFollowEx,才能将指定轴设定为FollowEx模式。一般应将从轴设定为FollowEx模式。

2. 如何设置主轴,从轴?

调用GTN_SetFollowMasterEx设置profile为从轴轴号,masterIndex为主轴轴号。

重要

为了减少跟随滞后,从轴的轴号应当大于主轴的轴号。

3. 如何设定从轴启动跟随条件?

所谓从轴启动跟随条件,是描述什么情况下从轴开始启动运动。有两种情况:第一,调用指令GTN_FollowStartEx以后从轴立即启动;第二,调用指令GTN_FollowStartEx以后,从轴还要等待主轴穿越了设定位置以后才启动跟随运动。用户需调用指令GTN_SetFollowEventEx来选择使用哪种跟随条件。

4. 如何设置Follow循环次数?

如果从轴的跟随运动是周期性的,用户可以只写入一个周期的运动规划到FIFO中,然后设定循环次数,即可实现周期性的Follow运动。调用指令GTN_SetFollowLoopEx即可。

5. 认识Follow模式的数据段类型。如何向Follow模式的FIFO中写入数据段?

调用指令GTN_FollowDataPercentEx,将数据段写入指定FIFO中。profile指从轴轴号,masterSegment 和slaveSegment分别指主轴和从轴应同时走过的位移,type指从轴的数据段类型,fifo是指定的FIFO编号。

提示

用户压入的数据段都是对位置点的描述,控制卡会根据用户选择的类型来自行计算速度。

6. FollowEx模式的数据段有4种类型。注意,都是针对从轴的规划。

(1) FOLLOW_SEGMENT_NORMAL表示普通段,FIFO中第1段的起点速度比率为0,从第2段起每段的起点速度比率等于上一段的终点速度比率。

(2) FOLLOW_SEGMENT_EVEN表示恒速比率段,FIFO中各段的段内速度比率保持不变。

(3) FOLLOW_SEGMENT_STOP表示停止段,该段的终点速度比率为0,起点速度比率根据段内位移和段内时间计算得到,和上一段的终点速度比率无关。

(4) FOLLOW_SEGMENT_CONTINUE表示连续段,FIFO中第一段的起点速度比率等于上个FIFO的终点速度比率,从第2段起每段的起点速度比率等于上一段的终点速度比率。

飞剪案例说明

用户如何根据自己主轴和从轴的速度比率及位置规划,来设计相应的数据段类型?可以参考下面的“飞剪案例”。
1. GTN_FollowSwitchNowEx切换FIFO指令:该指令能够切换Follow数据段或者切换Follow的FIFO。应当在匀速段调用该指令,否则会产生速度突跳。该指令有两种执行方式:立即执行和缓存区执行。
(1)立即模式:立即执行模式下,调用GTN_FollowSwitchNowEx指令立即切换Follow数据段、或者切换Follow的FIFO。Follow模式下有2个独立的FIFO用来保存数据。2个FIFO之间可以在运动状态下进行切换。下面描述一个切换FIFO的典型案例,如图所示。

Follow模式切换FIFO
黑色粗线为主轴规划,红色粗线为从轴规划。从轴的运动规律需要从“速度比率曲线1”变化到“速度比率曲线3”,为了实现从轴速度的平滑过渡,增加了一个“速度比率曲线2”的过渡状态。“速度比率曲线2”的起始速度比率和“速度比率曲线1”相等,“速度比率曲线2”的终点速度比率和“速度比率曲线3”相等。“速度比率曲线4”是“速度比率曲线1”、“速度比率曲线2”和“速度比率曲线3”的合成。具体的操作步骤是:
1) “速度比率曲线1”放入FIFO1中,把“速度比率曲线2”放入FIFO2中。假设当前正在运行的数据来自FIFO1,调用指令GTN_FollowSwitchNowEx,控制器会在FIFO1中的数据全部运行完后,自动切换去运行FIFO2中的数据,并且将FIFO1全部清空。
2) 在控制器运行FIFO2的数据的时候,调用指令GTN_FollowSpaceEx查询FIFO1是否被清空。如果已被清空,就将“速度比率曲线3”的数据存入FIFO1中。然后调用指令GTN_FollowSwitchNowEx,控制器会在FIFO2中的数据全部运行完后,自动切换去运行FIFO1中的数据,并且将FIFO2全部清空。
(2) 缓存区模式:缓冲区模式下,调用GTN_FollowSwitchNowEx指令将放入Follow的缓冲区。当Follow缓冲区执行到该指令时,如果当前数据段和GTN_FollowSwitchNowEx所位于的数据段不一致,该指令将不会执行。例如GTN_FollowSwitchNowEx位于Follow第2段,在GTN_FollowSwitchNowEx之前有GTN_FollowDelayEx或者GTN_FollowDiBitEx等模态指令。如果延时太长Follow已经执行到了第3段,当缓冲区执行到GTN_FollowSwitchNowEx指令时,将忽略该指令,继续执行剩余的缓冲区指令。
2. GTN_FollowDoBitEx:缓冲区DO指令,该指令可以在FollowEx数据段指令之前或之后调用。FollowEx数据段指令包括GTN_FollowDataPercentExGTN_FollowDataPercent2Ex。如果在Follow数据段之前调用该指令,进入该数据段立即执行DO输出。如果在Follow数据段之后调用该指令,当该数据段执行完毕时执行DO输出。
3. GTN_FollowDelayEx:缓冲区延时指令,该指令会阻塞FollowEx缓冲区的执行,如果该指令没有执行完毕,该指令之后的缓冲区指令不会执行。当设定的延时完成以后,才能继续执行FollowEx缓冲区的后续指令。该指令不会阻塞正常的FollowEx运动。
(1) 如果FollowEx设置为循环执行,当FIFO执行完毕时,无论延时是否到达都将结束延时指令,并立即执行剩余的缓冲区指令。
(2)该指令一般放在其它FollowEx缓冲区指令之间,例如FollowEx执行到某一段时打开DO,延时一段时间之后再关闭DO。
4. GTN_FollowDiBitEx:缓冲区DI等待指令,该指令会阻塞FollowEx缓冲区的执行,如果该指令没有执行完毕,该指令之后的缓冲区指令不会执行。当设定的DI触发,或者DI等待延时完成以后,才能继续执行FollowEx缓冲区的后续指令。该指令不会阻塞正常的FollowEx运动。

飞剪中的follow模式应用示例

剪应用背景介绍:简化的飞剪设备结构是主传送带(主轴)匀速拉着待剪物品定向移动,同时,在传送带上方装有一带切刀的转子,当转子旋转一周(逆时针为正向)时,刚好和待剪物体接触,使之剪断,剪切长度为:10000pulse,转子旋转一周为:8000pulse。如图所示。

飞剪模型
下面我们分析如何为这样一个同步机构设计FollowEx模式下的速度规划。
首先要找出同步机构的位置同步点。位置同步点表示主轴和从轴必须同时到达各自指定位置(比如被剪物体每走完上图中的“剪切长度”,转轮就要刚好走完一圈,两者在各自最终位置点上必须同时到达)。该例中,待剪物被切断时主轴和从轴的位置即位置同步点。要求主轴走完10000pulse时,从轴必须走完8000pulse。我们假设以切刀当前的位置来看,转轮还要正向运动2500个脉冲切刀才可达到位置同步点。
其次,查看该同步机构是否需要速度同步区。速度同步区表示在这段区域内主轴和从轴之间必须保持准确的速度比。该设备在待剪物被切断(位置同步点)前后一段距离内,需要有一速度同步区。在此速度同步区内,要求主轴和从轴速度相等。
因此,我们可以画出飞剪的主从轴速度曲线图,如图所示。
飞剪案例之Follow模式规划曲线
从上图来看,假设主轴(即传送带)是以Jog模式在运动,而从轴(即转轮)是Follow模式运动。区域1(红色阴影部分)是从轴启动跟随,表示从轴追赶主轴到达位置同步点的位移。区域2(黄色阴影部分)表示从轴旋转完整一周,回到起始点的位移,区域3(绿色阴影部分)与区域2一样,表示从轴循环旋转以达到等长切断主轴传送带上的被剪物体。
蓝色实心点即位置同步点,橙色方框区域为速度同步区。区域2曲线表示要求从轴上的切刀在与主轴上的被剪物体接触后保持一定时间的同速运行,以便转轮上的切刀切断主轴传送带上的被剪物。之后以较低速度和主轴物体分离;当切刀再次运动到临接近被切物体时,又要与主轴的速度同步,以此类推,循环运行。 如何实现以上规划曲线,现以上图的具体数字说明。
我们注意到,区域1和区域2是功能完全不同的数据段。区域1的数据段只是过渡段,当速度和位置到达预定值后便不再执行了,区域2则是需要循环执行的段,因此需要将区域1的数据放在一个FIFO,区域2的数据放在另外一个FIFO。
区域1:从轴追赶主轴的位移段,当主轴走完5000pulse时,从轴需要走2500pulse,如下表所示。以主轴规划位置为参考,该数据段的起点为规划0位置。
飞剪案例区域1的数据段
第一段(pulse)
主轴位置 5000
从轴位置 2500
区域2:可以分成5个数据段:第一段为切刀从位置同步点离开的速度同步区段;第二段为切刀减速脱离速度同步区段;第三段为从轴恒速段;第四段为从轴往主轴速度变化的加速段;最后一段是切刀接近被剪物体的速度同步区段。计算可得如下表所示。以主轴规划位置为参考,该数据段的起点为规划位置5000pulse。
飞剪案例区域2的数据段
第一段(pulse) 第二段(pulse) 第三段(pulse) 第四段(pulse) 第五段(pulse)
主轴位置 1000 4000 6000 9000 10000
从轴位置 1000 3400 4600 7000 8000
主轴位移长度 1000 3000 2000 3000 1000
从轴位移长度 1000 2400 1200 2400 1000

提示
  1. 压入控制器的数据为位置点(相对于数据段起点位置的位移),而不是位移长度。
  2. 位置点的起点都是以启动Follow运动(调用指令GTN_FollowStartEx之后)那一刻的位置点为零点(如设置 的启动条件为FOLLOW_EVENT_PASS,则以穿越点为零点基准)。
  3. 若切换了FIFO,位置点又是以换FIFO后的位置为零点。
FollowEx单FIFO模式示例

该例程主轴为Jog模式,速度为50pulse/ms。从轴为FollowEx模式,跟随主轴的规划位置。从轴启动的跟随条件是:主轴走过50000pulse后,从轴启动跟随。从轴的运动规律由3段组成,如下表所示:

Follow单FIFO数据段
第一段(pulse) 第二段(pulse) 第三段(pulse)
主轴位置 20000 20000 20000
从轴位置 10000 20000 10000
加速段跟随,匀速跟随,减速跟随,类似一个梯形曲线。并且无限次循环此数据段。主轴速度规划如图所示:
Follow单FIFO模式主轴速度规划
从轴速度规划如图所示:
Follow单FIFO模式从轴速度规划
// follow.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "conio.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:Follow跟随运动测试功能
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)将主轴切换为jog运动模式,设置主轴运动参数,将从轴设置为Follow跟随运动模式,启动主轴运动
//                当运动速度为50pulse/us,运动到50000个脉冲位置时,启动从轴进行无限循环跟随运动
//           (3)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                          // 指令返回值
    short core;                         // 需要执行例程的运动控制器核号
    short axis;                         // 需要初始化的轴起始索引号
    short axisCount;                    // 需要初始化的轴数量,从轴起始索引号开始算起
    long axisSts[2];                    // 轴状态
    unsigned long clock;                // 读取的控制器时钟
    double prfVel[2],prfPos[2];         // 读取的规划速度和位置
    TJogPrm jogPrm;                     // Jog运动参数
    double targetVel;                   // 目标速度
    short updateVelFlag=1;              // 更新速度的标致
    short stopFlag=1;                   // 更新速度的标致
    long mask,stopOption;

    short masterAxis = 1;               // 主轴索引
    short slaveAxis = 2;                // 从轴索引
    short masterType;                   // 主轴类型。
    short masterItem=0;                 // 输出位置类型,当masterType=GEAR_MASTER_AXIS(3)时起作用。
    short masterDir = 1;
    long masterPos;
    double slavePos;
    short fifo0 = 0;
    short space; 
    long loop;                          // 循环次数,0表示一直循环
    short moveDir;                      // 穿越方向
    long crossPos;                      // 穿越位置
    short percent,fifo;
    short segType;

    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 2;
    rtn = InitMc(core,axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开

    /*
    rtn = GTN_AxisOn(core,masterAxis);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn",rtn);
    }

    rtn = GTN_AxisOn(core,slaveAxis);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn",rtn);
    }
    */

    // 将主轴设置为Jog运动模式
    rtn = GTN_PrfJog(core,masterAxis);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PrfJog",rtn);
    }

    // 读取Jog运动参数(需要读取全部运动参数到上位机变量)
    memset(&jogPrm,0,sizeof(jogPrm));
    rtn = GTN_GetJogPrm(core,masterAxis,&jogPrm);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_GetJogPrm",rtn);
    }

    // 设置需要修改的运动参数
    jogPrm.acc = 1;
    jogPrm.dec = 1;
    // 设置Jog运动参数
    rtn = GTN_SetJogPrm(core,masterAxis,&jogPrm);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_SetJogPrm",rtn);
    }

    // 设置主轴的目标速度
    targetVel = 50;
    rtn = GTN_SetVel(core,masterAxis,targetVel);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_SetVel",rtn);
    }

    // 只启动主轴的运动
    axisCount = 1;
    rtn = GTN_UpdatePro(core,&masterAxis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_UpdatePro",rtn);
    }

    // 将从轴设为Follow运动模式
    rtn = GTN_PrfFollowEx(core,slaveAxis);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_PrfFollowEx",rtn);
    }

    // 清空从轴FIFO
    rtn = GTN_FollowClearEx(core,slaveAxis,fifo0);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowClearEx",rtn);
    }

    // 设置主轴,默认跟随主轴规划位置
    masterType = FOLLOW_MASTER_PROFILE;
    rtn = GTN_SetFollowMasterEx(core,slaveAxis, masterAxis,masterType,masterItem);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_SetFollowMasterEx",rtn);
    }

    // 查询Follow模式的剩余空间
    rtn = GTN_FollowSpaceEx(core,slaveAxis,&space);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowSpaceEx",rtn);
    }

    percent = 100;                 // S曲线
    fifo = 0;
    segType = FOLLOW_SEGMENT_NORMAL;

    // 向FIFO中增加运动数据
    masterPos = 20000;slavePos = 10000;
    rtn = GTN_FollowDataPercentEx(core,slaveAxis,masterPos,slavePos,segType,percent,fifo);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowDataPercentEx",rtn);
    }

    // 向FIFO中增加运动数据
    masterPos+= 20000;slavePos += 20000;
    rtn = GTN_FollowDataPercentEx(core,slaveAxis,masterPos,slavePos,segType,percent,fifo);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowDataPercentEx",rtn);
    }

    // 向FIFO中增加运动数据
    masterPos += 20000;slavePos += 10000;
    rtn = GTN_FollowDataPercentEx(core,slaveAxis,masterPos,slavePos,segType,percent,fifo);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowDataPercentEx",rtn);
    }

    // 设置循环次数,0为无限循环
    loop = 0;
    rtn = GTN_SetFollowLoopEx(core,slaveAxis,loop);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_SetFollowLoopEx",rtn);
    }

    // 设置启动跟随条件,主轴正向穿越50000时,从轴启动跟随
    moveDir = 1;
    crossPos = 50000;
    rtn = GTN_SetFollowEventEx(core,slaveAxis, FOLLOW_EVENT_PASS, moveDir, crossPos);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_SetFollowEventEx",rtn);
    }

    // 启动从轴Follow运动
    rtn = GTN_FollowStartEx(core,1<<(slaveAxis-1));
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowStartEx",rtn);
    }

    printf("\nPress Any Key To Exit !\n");

    axisCount = 2;
    while(!kbhit())
    {
        // 读取该轴的轴状态
        rtn = GTN_GetSts(core,axis,&axisSts[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetSts",rtn);
        }

        // 读取该轴的规划速度
        rtn =GTN_GetPrfVel(core,axis,&prfVel[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfVel",rtn);
        }

        // 读取该轴的规划位置
        rtn =GTN_GetPrfPos(core,axis,&prfPos[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfPos",rtn);
        }

        printf("主轴prfPos=%-10.1lf,主轴prfVel=%-10.1lf,从轴prfPos=%-10.1lf,从轴prfVel=%-10.1lf\r", 
            prfPos[0], prfVel[0], prfPos[1], prfVel[1]);
    } 

    // 平滑停止
    mask = 1 << (masterAxis - 1);
    mask |= 1 << (slaveAxis - 1);
    stopOption = 0;  
    rtn = GTN_Stop(core,mask,stopOption);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_Stop",rtn);
    }

    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*
    rtn = GTN_AxisOff(core,slaveAxis);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }

    rtn = GTN_AxisOff(core,masterAxis);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }
    */

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    return 0;
}

FollowEx双FIFO切换示例

该例程主轴为Jog模式,速度为50pulse/ms。从轴为Follow模式,跟随主轴的规划位置。从轴启动的跟随条件是:从轴在调用指令GTN_FollowStartEx后立即启动跟随。从轴在运动时更换跟随策略,其速度规划经过一个过渡的数据段,然后变成一个新的梯形曲线,并且无限次循环。如下面三个表所示。
我们把下表中的数据写入FIFO1中。数据见下表:

Follow双FIFO切换之原来的跟随策略
第一段(pulse) 第二段(pulse) 第三段(pulse)
主轴位置 20000 20000 20000
从轴位置 10000 20000 10000
把下表中的数据写入FIFO2中。数据见下表:
Follow双FIFO切换之更换跟随策略时的过渡段
第一段(pulse) 第二段(pulse) 第三段(pulse)
主轴位置 20000 20000 20000
从轴位置 10000 20000 16000
在运动过程中切换到FIFO2,同时在检查并确认FIFO1被控制器自动清空之后,将表中的数据写入FIFO1中,并切换运动FIFO1的数据。这样即可利用双FIFO切换完成跟随策略的更换。
Follow双FIFO切换之更换后的跟随策略
第一段(pulse) 第二段(pulse) 第三段(pulse)
主轴位置 20000 20000 20000
从轴位置 16000 20000 16000
主轴速度规划如图所示:
Follow双FIFO切换主轴速度规划
从轴速度规划如图所示:
Follow双FIFO切换从轴速度规划
  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
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
// follow.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "conio.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:Follow双FIFO切换功能
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)将主轴切换为jog运动模式,设置主轴运动参数,将从轴设置为Follow跟随运动模式,启动主轴运动
//                从轴在调用指令GTN_FollowStart后立即启动跟随。
//                从轴在运动时更换跟随策略,其速度规划经过一个过渡的数据段,然后变成一个新的梯形曲线,并且无限次循环
//           (3)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

#define STAGE_FIFO0             1
#define STAGE_TO_FIFO1          2
#define STAGE_TO_FIFO0          3
#define STAGE_END               4

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                          // 指令返回值
    short core;                         // 需要执行例程的运动控制器核号
    short axis;                         // 需要初始化的轴起始索引号
    short axisCount;                    // 需要初始化的轴数量,从轴起始索引号开始算起
    long axisSts[2];                    // 轴状态
    unsigned long clock;                // 读取的控制器时钟
    double prfVel[2],prfPos[2];         // 读取的规划速度和位置
    TJogPrm jogPrm;                     // Jog运动参数
    double targetVel;                   // 目标速度
    short updateVelFlag=1;              // 更新速度的标致
    short stopFlag=1;                   // 更新速度的标致
    long mask,stopOption;

    short masterAxis = 1;               // 主轴索引
    short slaveAxis = 2;                // 从轴索引
    short masterType;                   // 主轴类型。
    short masterItem=0;                 // 输出位置类型,当masterType=GEAR_MASTER_AXIS(3)时起作用。
    short masterDir = 1;
    long masterPos;
    double slavePos;
    short fifo0 = 0,fifo1 = 1;
    short space,stage,pressKey; 
    long loop;                          // 循环次数,0表示一直循环
    short moveDir;                      // 穿越方向
    long crossPos;                      // 穿越位置
    short percent;
    short segType;

    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 2;
    rtn = InitMc(core,axis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("InitMc",rtn);
    }

    // 轴上使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开

    /*
    rtn = GTN_AxisOn(core,masterAxis);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn",rtn);
    }

    rtn = GTN_AxisOn(core,slaveAxis);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn",rtn);
    }
    */

    // 将主轴设置为Jog运动模式
    rtn = GTN_PrfJog(core,masterAxis);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_PrfJog",rtn);
    }

    // 读取Jog运动参数(需要读取全部运动参数到上位机变量)
    memset(&jogPrm,0,sizeof(jogPrm));
    rtn = GTN_GetJogPrm(core,masterAxis,&jogPrm);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_GetJogPrm",rtn);
    }

    // 设置需要修改的运动参数
    jogPrm.acc = 1;
    jogPrm.dec = 1;
    // 设置Jog运动参数
    rtn = GTN_SetJogPrm(core,masterAxis,&jogPrm);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_SetJogPrm",rtn);
    }

    // 设置主轴的目标速度
    targetVel = 50;
    rtn = GTN_SetVel(core,masterAxis,targetVel);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_SetVel",rtn);
    }

    // 只启动主轴的运动
    axisCount = 1;
    rtn = GTN_UpdatePro(core,&masterAxis,axisCount);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_UpdatePro",rtn);
    }

    // 将从轴设为Follow运动模式
    rtn = GTN_PrfFollowEx(core,slaveAxis);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_PrfFollowEx",rtn);
    }

    // 清空从轴FIFO
    rtn = GTN_FollowClearEx(core,slaveAxis,fifo0);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowClearEx",rtn);
    }

    // 清空从轴FIFO0
    rtn = GTN_FollowClearEx(core,slaveAxis,fifo0);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowClearEx",rtn);
    }

    // 清空从轴FIFO1
    rtn = GTN_FollowClearEx(core,slaveAxis,fifo1);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowClearEx",rtn);
    }

    // 设置主轴,默认跟随主轴规划位置
    masterType = FOLLOW_MASTER_PROFILE;
    rtn = GTN_SetFollowMasterEx(core,slaveAxis, masterAxis,masterType,masterItem);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_SetFollowMasterEx",rtn);
    }

    // 查询Follow模式FIFO0的剩余空间
    rtn = GTN_FollowSpaceEx(core,slaveAxis,&space,fifo0);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowSpaceEx",rtn);
    }

    // 查询Follow模式FIFO1的剩余空间
    rtn = GTN_FollowSpaceEx(core,slaveAxis,&space,fifo1);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowSpaceEx",rtn);
    }

    percent = 100;                 // S曲线
    fifo0 = 0;
    segType = FOLLOW_SEGMENT_NORMAL;

    // 1、向FIFO0压入过渡的数据段
    // 向FIFO中增加运动数据
    masterPos = 20000;slavePos = 10000;
    rtn = GTN_FollowDataPercentEx(core,slaveAxis,masterPos,slavePos,segType,percent,fifo0);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowDataPercentEx",rtn);
    }

    // 向FIFO中增加运动数据
    masterPos+= 20000;slavePos += 20000;
    rtn = GTN_FollowDataPercentEx(core,slaveAxis,masterPos,slavePos,segType,percent,fifo0);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowDataPercentEx",rtn);
    }

    // 向FIFO中增加运动数据
    masterPos += 20000;slavePos += 10000;
    rtn = GTN_FollowDataPercentEx(core,slaveAxis,masterPos,slavePos,segType,percent,fifo0);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowDataPercentEx",rtn);
    }

    // 设置循环次数,0为无限循环
    loop = 0;
    rtn = GTN_SetFollowLoopEx(core,slaveAxis,loop);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_SetFollowLoopEx",rtn);
    }

    // 启动时间类型为FOLLOW_EVENT_START,调用GTN_FollowStartEx以后立即启动,moveDir、crossPos在该类型下无效
    moveDir = 1;
    crossPos = 50000;
    rtn = GTN_SetFollowEventEx(core,slaveAxis, FOLLOW_EVENT_START, moveDir, crossPos);
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_SetFollowEventEx",rtn);
    }

    // 启动从轴Follow运动
    rtn = GTN_FollowStartEx(core,1<<(slaveAxis-1));
    if(CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_FollowStartEx",rtn);
    }    

    pressKey = 0;
    axisCount = 2;
    stage = STAGE_FIFO0;
    while(1)
    {
        // 检查是否有按键
        if(kbhit())
        {
            getch();
            pressKey = 1;
        } 

        // 如果当前运行FIFO0中的数据并且检测到按键,则切换运行FIFO1中的数据
        if( STAGE_FIFO0 == stage )
        {
            if( 1 == pressKey )
            {
                pressKey = 0;
                // 准备切换到FIFO1运行
                stage = STAGE_TO_FIFO1;
                // 清空从轴FIFO1的数据
                rtn =GTN_FollowClearEx(core,slaveAxis,fifo1);
                if(CMD_SUCCESS != rtn)
                {
                    return CommandHandler("GTN_FollowClearEx",rtn);
                }

                // 向FIF1中增加运动数据
                segType = FOLLOW_SEGMENT_CONTINUE;
                masterPos = 20000;slavePos = 10000;
                rtn = GTN_FollowDataPercentEx(core,slaveAxis,masterPos,slavePos,segType,percent,fifo1);
                if(CMD_SUCCESS != rtn)
                {
                    return CommandHandler("GTN_FollowDataPercentEx",rtn);
                }

                // 向FIF1中增加运动数据
                segType = FOLLOW_SEGMENT_NORMAL;
                masterPos+= 20000;slavePos += 20000;
                rtn = GTN_FollowDataPercentEx(core,slaveAxis,masterPos,slavePos,segType,percent,fifo1);
                if(CMD_SUCCESS != rtn)
                {
                    return CommandHandler("GTN_FollowDataPercentEx",rtn);
                }

                // 向FIF1中增加运动数据
                segType = FOLLOW_SEGMENT_NORMAL;
                masterPos += 20000;slavePos += 16000;
                rtn = GTN_FollowDataPercentEx(core,slaveAxis,masterPos,slavePos,segType,percent,fifo1);
                if(CMD_SUCCESS != rtn)
                {
                    return CommandHandler("GTN_FollowDataPercentEx",rtn);
                }

                // 切换到FIFO1
                // 当前工作中的FIFO0的数据遍历完以后才会切换FIFO1
                rtn = GTN_FollowSwitchEx(core,1<<(slaveAxis-1));
                if(CMD_SUCCESS != rtn)
                {
                    return CommandHandler("GTN_FollowSwitchEx",rtn);
                }
            }
        }

        // 检查FIFO0中的数据段是否执行完成,如果已执行完成,则将新的速度规划传入FIFO0中,
        // FIFO1中的数据执行完成后,立即切换运行FIFO0中的数据。
        if( STAGE_TO_FIFO1 == stage )
        {
            // 查询FIFO0的剩余空间
            rtn = GTN_FollowSpaceEx(core,slaveAxis,&space,fifo0);
            if(CMD_SUCCESS != rtn)
            {
                return CommandHandler("GTN_FollowSpaceEx",rtn);
            }

            // 如果FIFO0执行结束,space = 16,说明已经切换到FIFO1
            if( 16 == space )
            {
                // 准备切换到FIFO0运行
                stage = STAGE_TO_FIFO0;

                // 向FIF0中增加运动数据
                segType = FOLLOW_SEGMENT_CONTINUE;
                masterPos = 20000;slavePos = 16000;
                rtn = GTN_FollowDataPercentEx(core,slaveAxis,masterPos,slavePos,segType,percent,fifo0);
                if(CMD_SUCCESS != rtn)
                {
                    return CommandHandler("GTN_FollowDataPercentEx",rtn);
                }

                // 向FIF0中增加运动数据
                segType = FOLLOW_SEGMENT_NORMAL;
                masterPos+= 20000;slavePos += 20000;
                rtn = GTN_FollowDataPercentEx(core,slaveAxis,masterPos,slavePos,segType,percent,fifo0);
                if(CMD_SUCCESS != rtn)
                {
                    return CommandHandler("GTN_FollowDataPercentEx",rtn);
                }

                // 向FIF0中增加运动数据
                segType = FOLLOW_SEGMENT_NORMAL;
                masterPos += 20000;slavePos += 16000;
                rtn = GTN_FollowDataPercentEx(core,slaveAxis,masterPos,slavePos,segType,percent,fifo0);
                if(CMD_SUCCESS != rtn)
                {
                    return CommandHandler("GTN_FollowDataPercentEx",rtn);
                }

                // 切换到FIFO0
                // 当前工作中的FIFO1的数据遍历完以后才会切换FIFO0
                rtn = GTN_FollowSwitchEx(core,1<<(slaveAxis-1));
                if(CMD_SUCCESS != rtn)
                {
                    return CommandHandler("GTN_FollowSwitchEx",rtn);
                }
            }
        }

        if( STAGE_TO_FIFO0 == stage )
        {
            // 查询FIFO1的剩余空间
            rtn = GTN_FollowSpaceEx(core,slaveAxis,&space,fifo1);
            if(CMD_SUCCESS != rtn)
            {
                return CommandHandler("GTN_FollowSpaceEx",rtn);
            }

            // 如果FIFO1执行结束,space = 16,说明已经切换到FIFO0
            if( 16 == space )
            {
                stage = STAGE_END;
            }
        }

        if( STAGE_END == stage )
        {
            if( 1 == pressKey )
            {
                pressKey = 0;
                break;
            }
        }

        // 读取该轴的轴状态
        rtn = GTN_GetSts(core,axis,&axisSts[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetSts",rtn);
        }

        // 读取该轴的规划速度
        rtn =GTN_GetPrfVel(core,axis,&prfVel[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfVel",rtn);
        }

        // 读取该轴的规划位置
        rtn =GTN_GetPrfPos(core,axis,&prfPos[0],axisCount,&clock);
        if ( CMD_SUCCESS != rtn )
        {
            return CommandHandler("GTN_GetPrfPos",rtn);
        }

        printf("主轴prfPos=%-10.1lf,主轴prfVel=%-10.1lf,从轴prfPos=%-10.1lf,从轴prfVel=%-10.1lf\r", 
            prfPos[0], prfVel[0], prfPos[1], prfVel[1]);

    }

    // 平滑停止
    mask = 1 << (masterAxis - 1);
    mask |= 1 << (slaveAxis - 1);
    stopOption = 0;  
    rtn = GTN_Stop(core,mask,stopOption);
    if ( CMD_SUCCESS != rtn )
    {
        return CommandHandler("GTN_Stop",rtn);
    }

    // 轴规划完成后,下使能(默认代码屏蔽,接实际电机时才调用)
    // 轴下使能,如果需要测试实际驱动器、电机的运动,请将此注释代码打开
    /*
    rtn = GTN_AxisOff(core,slaveAxis);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }

    rtn = GTN_AxisOff(core,masterAxis);
    if (CMD_SUCCESS != rtn)
    {
        return CommandHandler("GTN_AxisOn", rtn);
    }
    */

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    return 0;
}

1.6 插补运动模式

插补运动模式主要应用于需要多轴联动的连续轨迹加工场合,如金属加工、激光切割、点胶等行业。运动控制器的插补运动模式主要具有以下功能:

1. 可以实现直线、圆弧、螺旋线、椭圆等插补运动。

2. 具有前瞻预处理功能,能够实现小线段高速平滑的连续轨迹运动。

3. 每个坐标系含有两个缓存区,可以实现缓存区暂停、恢复、倍率等功能。

4. 支持插补指令高速传输即DMA功能。

5. 支持插补运动平滑功能。

6. 支持手轮引导功能。

7. 具有缓存区延时、缓冲区数字量输出、缓冲区单轴运动等功能。

1.6.1 运动控制器的插补模式

1.6.1.1 指令列表

插补运动指令

指令 说明
GTN_SetCrdMapBase 设置插补坐标系基础映射规划轴。
GTN_GetCrdMapBase 读取插补坐标系基础映射规划轴。
GTN_SetCrdPrm 设置坐标系参数,确立坐标系映射,建立坐标系。
GTN_GetCrdPrm 读取坐标系参数。
GTN_CrdClear 清除插补缓存区内的插补数据。
GTN_CrdDataEx 插补缓存区数据结束指令。
GTN_CrdStart 启动插补运动。
GTN_CrdSpace 读取插补缓存区剩余空间。
GTN_CrdStatus 读取插补坐标系状态。
GTN_GetRemainderSegNum 读取未完成的插补段段数。
GTN_GetCrdPos 读取坐标系的当前坐标位置值。
GTN_GetCrdVel 读取插补坐标系当前的运动速度。
GTN_SetUserSegNumEx 设置自定义插补段段号。
GTN_GetUserSegNum 读取自定义插补段段号。
GTN_SetCrdStopDec 设置插补运动平滑停止、急停合成加速度。
GTN_GetCrdStopDec 读取插补运动平滑停止、急停合成加速度。。
GTN_SetBufCmdStopMode 设置插补用户段指令的停止模式。
GTN_GetBufCmdStopMode 获取插补用户段指令的停止模式。
GTN_SetBufIoHoldValue 设置插补运动暂停或异常停止时,do保持输出特定值。
GTN_GetBufIoHoldValue 读取插补运动暂停或异常停止时,do保持输出特定值。
GTN_SetCrdPrintLogEnable 设置插补打印log信息使能标志。
GTN_GetCrdPrintLogEnable 获取插补打印log信息使能标志。

1.6.1.2 重点说明

实现插补运动的基本步骤如下:

1. 建立插补坐标系。

2. 设置前瞻参数以实现高速平滑的连续轨迹运动。

3. 向插补缓存区压入直线、圆弧等插补运动指令。

4. 调用插补缓存区数据结束指令。

5. 启动运动,运动控制卡依次执行缓存区的插补数据,直到所有的插补数据全部运动完成。

1. 建立插补坐标系

在运动控制器的初始状态下,复位之后或者还未使用过插补运动状态下,所有的规划轴都处于单轴运动模式下,插补坐标系是无效的。所以,进行插补运动时,首先需要建立坐标系,将规划轴映射到相应的坐标系中。每个坐标系最多支持八维,用户根据自己的需求,也可以使用二维(X-Y)、三维(X-Y-Z)坐标系描述运动轨迹。

用户通过调用指令GTN_SetCrdPrm将在坐标系内描述的运动通过映射关系映射到相应的规划轴上。运动控制卡根据坐标映射关系,控制各轴运动,实现要求的运动轨迹。调用指令GTN_SetCrdPrm时,所映射的各规划轴必须处于静止状态。

插补坐标系映射的规划轴默认只能为前8个规划器,如果需要使用第8个以上的规划器,可以通过调用指令GTN_SetCrdMapBase设置基础映射轴,使得GTN_SetCrdPrm建立坐标系时,能够支持基础映射轴开始后的连续8个规划器。

建立坐标系示例

建立了一个二维坐标系,规划轴1对应为x轴,规划轴2对应为y轴,坐标系原点的规划位置是(100, 100),单位:pulse,在此坐标系内运动的最大合成速度为500pulse/ms,最大合成加速度为1pulse/ms2,最小匀速时间为50ms。如图所示。

加工坐标系偏移量示意图
坐标系参数TCrdPrm重点说明: 1. dimension:表示所建立的坐标系的维数,取值范围:[1, 8],示例中所建立的坐标系是二维,即X-Y坐标系。
2. profile[0..7]:表示插补坐标系与规划器的映射关系,示例中profile[0]=1,profile[1]=2,其他值为0。
3. synVelMax:表示该坐标系所能承受的最大合成速度,如果用户在输入插补段的时候所设置的目标速度大于了该速度,则将会被限制为该速度。
4. synAccMax:表示该坐标系所能承受的最大合成加速度,如果用户在输入插补段的时候所设置的加速度大于了该加速度,则将会被限制为该加速度。
5. setOriginFlag:表示是否需要指定坐标系原点的规划位置,以方便用户建立区别于机床坐标系的加工坐标系。0:不需要指定原点坐标值,则坐标系的原点在当前规划位置上。1:需要指定原点坐标值,坐标系的原点在originPos指定的规划位置上。示例中,设置setOriginFlag为1。
6. originPos[0..7]:指定的坐标系原点的规划位置值。

2. 初始化前瞻

前瞻初始化详细说明请参考1.6.2章节

3. 压入插补数据

运动控制器插补运动模式采用缓存区运动方式,提供了4096段的缓存区空间,用户需要向插补缓存区中传递插补数据,在启动插补运动后,运动控制器会依次执行用户所传递的插补数据,直到所有的插补数据全部运动完成。

在插补段数据量比较大的时候,用户需要配合GTN_CrdSpace指令查询插补缓存区的剩余空间,在有空间的时候再调用缓存区指令传递数据,如果插补缓存区已满,调用缓存区指令将会返回错误,说明该段插补数据没有输入成功,需要再次输入该段插补数据。

当用户所有的插补数据已经输入完毕,需要调用指令GTN_CrdDataEx直到该指令返回0,以把所有残存于上位机的数据(由于前瞻或者高速通讯功能)压入控制器。

4. 启动运动

当插补缓冲区已经有了一定量的插补数据时,就可以通过调用指令GTN_CrdStart启动运动了。

在压入一定数据量的插补数据后,由于存在前瞻或者高速通讯功能,不一定所有调用的指令都已经真正到达运动控制卡的插补缓存区,用户可以通过调用指令GTN_GetRemainderSegNum查询到插补缓存区的数据段数,在确认插补缓存区已经存在了一定量的插补数据后,再启动运动。建议已经压入缓存区的插补数据足够运动一段时间再启动运动,如果数据段太少就启动,容易导致缓存区跑空。

5. 插补数据发送流程

对于实际应用来说,加工的插补数据段很有可能会超过控制卡缓冲区大小,不能一次压入到缓冲区,因此需要在一边执行插补运动过程中一边压入插补数据段。

在刚开始时,需要先向插补缓冲区压入一定数据量的插补数据,确认插补缓冲区已经有一定量的数据了再启动插补运动,然后在插补运动过程中判断插补缓冲区是否有空间,若有空间则继续压入插补数据。直到当加工数据都通过插补指令压入完毕了,调用GTN_CrdDataEx直到返回0,把所有残存于上位机的数据压入控制卡。调用GTN_CrdDataEx直到返回0的过程中,不需要一直死循环等待,可以边执行其他操作,周期调用该指令直到返回0就不再调用。流程图如图所示。

插补流程图
插补运动

1.6.2 插补前瞻

1.6.2.1 指令列表

前瞻指令列表

指令 说明
GTN_SetupLookAheadCrd 建立前瞻坐标系,设置机床类型。
GTN_SetMinDccAngleLa 设置最小减速到零角度。
GTN_SetMinEvenVelTimeLa 设置最小匀速时间。
GTN_SetFollowAxisParaLa 设置跟随轴参数。
GTN_SetAxisLimitModeLa 设置各个轴运动能力限制模式。
GTN_SetAxisVelValidModeLa 设置合成速度对哪些轴生效。
GTN_SetRadiusRatioTableLa 设置前瞻曲率参数表。
GTN_SetAngleCoefTableLa 设置前瞻时间参数表。
GTN_SetMaxOverrideLa 设置插补运动允许的最大速度倍率。
GTN_SetAxisScale 设置轴的脉冲当量。
GTN_InitLookAheadEx 初始化前瞻预处理模块参数。
GTN_InitLookAheadPara 读取插补坐标系当前的运动速度。
GTN_SetUserSegNumEx 初始化前瞻预处理模块参数的简化指令。
GTN_GetMotionTimeEx 读取插补运动预估时间。
GTN_SetPathOptimizePrmLa 设置轨迹优化参数。

1.6.2.2 重点说明

提示

前瞻模块的位置、速度、加速度单位均为物理单位,即mm、mm/s、mm/s2

前瞻模块是一个预处理模块,会通过前瞻多段轨迹信息,自动根据轨迹段的特征对插补运动速度进行优化和限制,以解决连续小线段轨迹加工时,由于频繁加减速或者不减速造成的加工效率低或者机械振动的问题,在保证运动平稳的前提下提高加工效率。

下面用一个实例来说明前瞻机制的优势。假设机床要加工一个长方形的零件,刀具所走的轨迹如图所示。假设m点到n点距离30mm,由30段1mm的小直线段组成。n点到p点距离20mm,由20段1mm的小直线段组成。

使用前瞻与不使用前瞻的速度规划区别

如果按照图(b)所示的速度规划,即在拐角处不减速,则加工精度一定会较低,而且可能在拐弯时对刀具和零件造成较大冲击。如果按照图(c)所示的速度规划,即在拐角处减速为0,可以最大限度保证加工精度,但加工速度就会慢下来。如果按照图(d)所示的速度规划,在拐角处将速度减到一个合理值,既可以满足加工精度又能提高加工速度,就是一个好的速度规划。

为了实现类似图(d)所示的好的速度规划,前瞻模块不仅要知道当前运动的位置参数,还要提前知道后面若干段运动的位置参数,这就是所谓的前瞻。例如在对图(a)中的轨迹做前瞻处理时,我们设定控制器预先读取50段运动轨迹到缓存区中,则它会自动分析出在第30段将会出现拐点,并依据用户设定的转角系数计算在拐弯处的终点速度。前瞻处理模块也会依照用户设定的最大加速度值计算速度规划,使得在拐角处的速度不会超过允许的终点速度,防止对机械部分造成冲击。

插补缓存区是运动控制器内部专门用于插补运动的缓存区资源,大小4096段,每一段可以放一条指令。当前瞻段数不为0时,用户调用缓存区指令传递的插补数据先进入前瞻预处理模块,该模块会前瞻一定段数的轨迹信息以自动处理出当前插补数据的合理速度,再进入插补缓存区。

如果用户所有的插补数据已经输入完毕,由于前瞻模块中还有数据没有进入插补缓存区,这时,需要调用GTN_CrdDataEx(1, NULL, 0),运动控制器会将前瞻模块中的数据依次传递给插补缓存区,直到所有数据全部进入插补缓存区。

注意

连续调用缓冲区辅助运动指令:目前支持最多连续调用16条缓冲区辅助运动指令(例如:GTN_BufIOExGTN_BufGearEx……等非插补运动指令)。

实现前瞻预处理的基本步骤如下:

1. 调用指令GTN_SetupLookAheadCrd设置前瞻模型。

2. 根据工艺需要调用相关的前瞻指令设置对应的参数,如无特殊要求,也可以不设置。

3. 调用指令GTN_InitLookAheadEx设置初始化前瞻参数。

4. 向插补缓存区压入直线、圆弧等插补运动指令。

5. 调用插补缓存区数据结束指令GTN_CrdDataEx

1. 设置前瞻模型

(1) 标准三轴

机台为标准的3轴正交模型时,设置前瞻模型为标准三轴模式。前瞻模块会自动根据加工轨迹信息和前瞻参数预处理出合理的速度参数。

标准三轴模式并不意味着必须建立三轴插补坐标系,用户可以建立二轴、三轴、四轴甚至八轴插补,其中,满足笛卡尔坐标系的轴,前瞻会根据这些轴描述的三维轨迹分析轨迹特征,预处理出合理的速度参数,而其他非笛卡尔坐标系的轴,仅进行跟随运动,通过设置相关的前瞻指令,也可以对跟随轴进行速度约束。

(2) 五轴

机台为标准的五轴模型时,设置前瞻模型为五轴模式。五轴模型需要配置机床类型以及相关的机构参数,前瞻模块会根据机床参数进行运动学解算,根据加工轨迹的几何特征以及各轴的运动约束预处理出合理的速度参数,具体应用请参考相关的五轴功能介绍文档。

(3) 多轴

机台为多轴设备,且各个轴之间并不存在笛卡尔坐标系关系或者不是标准的五轴结构,设置前瞻模型为多轴模式。前瞻模块会根据各个轴的轴约束信息进行合理的速度处理,保证多轴联动且运动平稳。

2. 初始化前瞻

前瞻预处理模块会自动根据多段轨迹段的几何特征,如夹角,曲率信息对速度进行约束,同时会根据机台的运动能力,包括各轴的最大速度,最大加速度等信息,对速度进行合理的规划。

初始化前瞻参数重点说明:

  • lookAheadNum:前瞻段数,表示前瞻轨迹的段数,一般建议设置为200段。

如果轨迹段的段长都很短时,则需要的前瞻段数越大,当前瞻段数不够大时,会出现合成速度不断加速再减速的现象,此时,用户可以根据需要增大前瞻段数。

  • time:转角系数。

前瞻模块会预看转角大小,根据转角大小和转角系数计算出该角度的转角速度,提前减速至该速度,通过转角后,再次加速到指令设置的目标速度。

转角分解图

转角和转角速度的关系为:

转角系数决定了转角处允许的速度变化量∆V的大小,进而决定转角处的速度。转角系数越小,表示转角处允许的速度变化量越小,为了达到较小的速度变化量,转角处的速度就会降的更低。反之,转角系数越大,则转角处的速度就会越高。

考虑到机台的滞后特性,转角速度越低,机台滞后越小,转角的精度会越好;反之,转角速度越高,精度会越差。同时,转角速度越低,对机台造成的振动越小;反之,转角速度越高,对机台造成的振动越大。

  • radiusRatio:圆弧系数。

在圆弧或者曲线插补时,即使以恒速运动,由于轨迹方向不断变化,会产生向心加速度,且相同速度情况下,圆弧半径越小,产生的加速度越大,加速度过大时,机台跟随差,会带来较大的精度损失。前瞻模块会预看轨迹,根据圆弧半径不断调整对应半径下的目标速度,保证产生的加速度小于允许加速度,从而避免由于速度过大导致精度变差。

圆弧系数的意义可通过指定一个参考圆Rref希望达到多大的速度Vref决定,关系式如下:

假设指定参考圆半径为5mm,希望半径5mm的圆弧能够达到3000mm/min的速度,则可设置radiusRatio为1。圆弧系数确定后,任何半径的圆弧速度均满足如下关系式:

-

vMax[0...7]:各轴最大速度。

如果用户设置的目标速度会导致某个坐标轴的速度大于设置的单轴最大速度值,则控制卡会对自动降低目标速度,保证所有轴的速度均不会超过该指令设置的各轴最大速度。

  • aMax[0...7]:各轴最大加速度。

如果用户设置的加速度会导致某个坐标轴的加速度大于设置的单轴最大加速度值,则控制卡会对自动降低加速度,保证所有轴的加速度均不会超过该指令设置的各轴最大加速度。

  • dvMax[0...7]:各轴最大速度跳变量。

前瞻模块会根据各轴允许的最大速度跳变量计算出转角允许的最大速度,提前减速至该速度,通过转角后,再次加速到指令设置的目标速度。

直角加工分解图

假设加工上图中的直角,设置了X,Y轴的最大速度跳变量限制生效,且最大速度跳变量为△VXmax,△VYmax,转角速度为V时,由于在拐角处,X,Y轴的速度跳变量为△VX=|V-0|=V,△VY=|0-V|=V,为保证两轴速度跳变量均小于设置的最大速度跳变量,则转角速度需满足V≤min(△VXmax,△VYmax)。

当前瞻模型为标准三轴模型时,通过前瞻参数中拐角系数和曲率系数已经能够对速度进行合理的约束,vMax[0…7],aMax[0…7],dvMax[0…7]等轴参数默认无效;而当前瞻模型为五轴或者多轴模型时,这些轴参数默认生效。如果需要修改参数的生效模式,可以通过调用指令GTN_SetAxisLimitModeLa来切换。

3. 运动时间预估

如果用户希望在真正执行运动前,就预先知道插补运动的总执行时间,可以在设置初始化前瞻指令GTN_InitLookAheadEx时开启运动时间预估模式。

用户只需按照正常流程发送插补缓冲区的指令,在插补指令发送完毕后,调用GTN_CrdDataEx并返回正确后,调用GTN_GetMotionTimeEx得到所有插补缓冲区指令的执行时间。

提示
  1. 运动时间的预估仅包括插补运动指令、延时指令和模态点位运动指令。
  2. 开启运动时间预估模式,预估的运动时间为使用最大速度倍率的速度所运动的时间,如果需要使用插补指令中设置的速度,或者设置速度的指定倍数,需要通过调用指令GTN_SetMaxOverrideLa将最大速度倍率设置为1或指定倍数。

4. 轨迹优化

工业生产中圆弧经常被细分成连续多段小线段进行加工,如果不做任何优化处理,即便保证坐标系合成速度不变的情况,单轴速度也是不断抖动的;而使用轨迹优化功能,可以智能识别轨迹的理想形态并对轨迹进行优化处理,保证单轴速度连续变化。

调用指令GTN_SetPathOptimizePrmLa可以开启轨迹优化功能。

插补轨迹优化示例

使用小线段模拟圆并加工,圆半径设置为10mm,以弦差为0.005划分圆,分为100段小线段,生成101个XY坐标点作为直线插补GTN_LnXYEx的目标位置,设置轴的脉冲当量为1000脉冲每毫米,插补速度为100mm/s,插补加速度为10000mm/s2,前瞻的拐角系数为0.01,前瞻圆弧参数为5。
1. 没有使用轨迹优化的插补轨迹图和插补合成速度图(左边为XY轴插补运动轨迹,右边为XY轴插补合成速度): 2. 没有使用轨迹优化的单轴速度图:

X轴速度
Y轴速度
使用小线段拟合圆的插补轨迹图和插补合成速度图(没有使用轨迹优化)
可以看见X轴和Y轴单轴速度是阶梯形状的。这样的速度在实际加工中必然会对机械系统造成冲击。
设置轨迹优化的误差为0.03mm,可以看见X轴和Y轴的单轴速度优化后是平滑的,加工更平稳。由于设置了blending平滑的参数为0.03mm,可以看见直线和圆连接的拐角处也被平滑。
3. 使用轨迹优化的插补轨迹图和插补合成速度图(左边为XY轴插补运动轨迹,右边为XY轴插补合成速度): 4. 使用轨迹优化的单轴速度图:
X轴速度
Y轴速度
使用小线段拟合圆的插补轨迹图和插补合成速度图(使用轨迹优化)
// CrdLine.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

// 该例程仅用于功能演示,请保证安全的情况下使用

// 测试功能:插补直线运动示例
// 测试平台:网络型运动控制器
// 测试环境:Windows
// 测试流程:
//           (1)初始化控制器
//           (2)建立插补坐标系
//           (3)初始化前瞻
//           (4)调用轨迹优化函数GTN_SetPathOptimizePrmLa
//           (5)压直线插补数据
//           (5)启动插补坐标系运动
//           (6)检测插补是否运动完成
//           (7)运动完成,关闭控制器
// 注意事项:
//           (1)本例程使用的“例程专用.xml”、“例程专用.cfg”,仅用于本例程
//           (2)实际使用时,需要使用MotionStudio生成网络配置xml

// 加载固高运动控制库头文件
#include "gxn.h"
// 动态加载固高运动控制gxn.lib库
#pragma comment(lib,"gxn.lib")

/**
 * @brief 指令出错打印函数
 * @param command 打印信息字符串
 * @param error 错误码
 * @return 错误码
*/
short CommandHandler(char* command, short error)
{
    printf("%s = %d\n", command, error);
    getchar();

    return error;
}

/**
 * @brief 初始化运动控制器(开卡 + 初始化网络拓扑 + 初始化轴)
 * @param core 需要初始化的核号,从1开始
 * @param axis 需要初始化的轴起始索引,从1开始
 * @param axisCount 需要初始化的轴数量,从起始索引axis开始计数,必须大于0
 * @return 0表示初始化成功,非0表示初始化失败
*/
short InitMc(short core,short axis,short axisCount)
{
    short rtn;
    short overTime;
    long status;

    // 打开运动控制器
    rtn = GTN_OpenCard(CHANNEL_PCIE,NULL,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_OpenCard",rtn);
    }
    printf("Open Card Success !\n");

    // 初始化网络
    // 注意:(1)“例程专用.xml”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的网络配置文件
    // overTime:网络初始化超时时间,单位:秒
    overTime = 120;
    rtn = GTN_NetInit(NET_INIT_MODE_XML_STRICT,"例程专用.xml",overTime,&status);
    if ( 0 != rtn )
    {
        printf("status = %d\n",status);
        return CommandHandler("GTN_NetInit",rtn);
    }
    printf("Init Net Success !\n");

    // 加载配置文件到控制器
    // 注意:(1)“例程专用.cfg”仅用于本例程
    //       (2)实际使用时,需要使用MotionStudio生成对应的配置文件
    rtn = GTN_LoadConfig(core,"例程专用.cfg");
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LoadConfig(\"例程专用.cfg\")",rtn);
    }

    // 清除轴状态
    rtn = GTN_ClrSts(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_ClrSts",rtn);
    }
    printf("Init Mc Config Success !\n");

    return rtn;
}

/**
 * @brief 建立插补坐标系,并且初始化前瞻
 * @param core 插补坐标系所在的核号
 * @param crd 插补坐标系号
 * @param fifo 插补坐标系缓存区号
 * @return 0表示成功,非0表示失败
*/
short InitCrd(short core,short crd,short fifo)
{
    short rtn;
    short i;
    TCrdPrm crdPrm;                    // 插补坐标系参数

    EMachineMode machineMode;          // 插补坐标系机床模式
    TLookAheadParameter laPrm;         // 前瞻参数
    short motionMode;                  // 前瞻运行模式

    // 建立插补坐标系
    memset(&crdPrm,0,sizeof(crdPrm));  // 将结构体参数全部初始化为0
    crdPrm.dimension = 2;              // 二维坐标系
    crdPrm.profile[0] = CRD_AXIS_X;    // x轴,此处为规划器1为坐标系的x轴
    crdPrm.profile[1] = CRD_AXIS_Y;    // y轴,此处为规划器2为坐标系的y轴
    crdPrm.synVelMax = 500;            // 最大合成速度,单位:脉冲/ms
    crdPrm.synAccMax = 10;             // 最大合成加速度,单位:脉冲/ms^2
    crdPrm.evenTime = 0;               // 最小匀速时间,单位:ms
    crdPrm.setOriginFlag = 0;          // 坐标系原点设置标志,0:使用当前规划位置作为坐标系原点
                                       //                     1:使用crdPrm.originPos[8]中设置的位置作为坐标系原点
    rtn = GTN_SetCrdPrm(core,crd,&crdPrm);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_SetCrdPrm",rtn);
    }

    // 建立前瞻坐标系
    machineMode = NORMAL_THREE_AXIS;   // 标准三轴机床模式
    rtn = GTN_SetupLookAheadCrd(core,crd,machineMode);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_SetupLookAheadCrd",rtn);
    }

    // 初始化前瞻参数
    memset(&laPrm,0,sizeof(laPrm));    // 将结构体参数全部初始化为0
    laPrm.lookAheadNum = 200;          // 前瞻段数,前瞻工作区每攒够当前数量的运动段就会进行一次预处理,输出处理后的数据
    laPrm.time = 0.001;                // 时间常数,单位:ms
    laPrm.radiusRatio = 1.5;           // 曲率限制调节系数
    for (i=0;i<8;++i)                  // 数据索引,i=0表示x轴,i=1表示y轴,i=7表示w轴
    {
        laPrm.vMax[i] = 500;           // 当前轴最大速度限制,单位:mm/s
        laPrm.aMax[i] = 2000;          // 当前轴最大加速度限制,单位:mm/s^2
        laPrm.DVMax[i] = 300;          // 当前轴在时间常数内最大速度变化量
        laPrm.scale[i] = 1000;         // 当前轴当量,单位:脉冲/mm
        laPrm.axisRelation[i] = i+1;   // 一般按此配置,无需修改
    }
    motionMode = 0;                    // 保留参数,必须为0
                                       // TPreStartPos为保留参数,必须为NULL
    rtn = GTN_InitLookAheadEx(core,crd,&laPrm,fifo,motionMode,NULL);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_InitLookAheadEx",rtn);
    }

    // 清空插补缓冲区
    rtn = GTN_CrdClear(core,crd,fifo);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_CrdClear",rtn);
    }

    return rtn;
}

/**
 * @brief 获取插补运动状态
 * @param core 插补坐标系所在的核号
 * @param crd 插补坐标系号
 * @param fifo 插补坐标系缓存区号
 * @param pCrdRun 插补坐标系运动状态变量指针
 * @return 0表示获取插补运动状态成功,非0表示获取插补运动状态出错
*/
short GetMotionInfo(short core,short crd,short fifo,short *pCrdRun)
{
    short rtn;
    long crdExecuteSegNum;             // 插补已经执行的段数
    long crdRemainder;                 // 插补剩余未执行段数
    double crdPos[8];                  // 插补规划位置

    // 运动过程中读取当前状态
    rtn = GTN_CrdStatus(core,crd,pCrdRun,&crdExecuteSegNum,fifo);
    rtn += GTN_GetCrdPos(core,crd,&crdPos[0]);
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_CrdStatus",rtn);
    }
    printf("crdRun = %d, crdExecuteSegNum = %d, xPos = %lf, yPos = %lf \r",*pCrdRun,crdExecuteSegNum,crdPos[0],crdPos[1]);

    if ( 0 == *pCrdRun )
    {
        rtn = GTN_GetRemainderSegNum(core,crd,&crdRemainder,fifo);
        if ( 0 != rtn )
        {
            return CommandHandler("GTN_GetRemainderSegNum",rtn);
        }

        if ( 0 != crdRemainder )
        {
            printf("\nCrd Run Empty Error!\n");
        }
        else
        {
            printf("\nCrd Motion Done !\n");
        }
    }

    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    short rtn;                         // 指令返回值
    short core;                        // 需要执行例程的运动控制器核号
    short axis;                        // 需要初始化的轴起始索引号
    short axisCount;                   // 需要初始化的轴数量,从轴起始索引号开始算起

    // 初始化运动控制器
    // 开卡 + 初始化网络拓扑 + 初始化核1的1-8轴
    core = 1;
    axis = 1;
    axisCount = 8;
    rtn = InitMc(core,axis,axisCount);
    if ( 0 != rtn )
    {
        return rtn;
    }

    short crd;                         // 插补坐标系号
    short fifo;                        // 插补数据使用的缓冲区号

    crd = 1;                           // 指定测试的插补坐标系号为1
    fifo = 0;                          // 指定测试的插补坐标系缓冲区号为0
                                       // 注意:(1)每套插补坐标系只有fifo=0时支持前瞻,fifo=1时不支持前瞻
                                       //       (2)fifo=0一般用于轨迹加工
                                       //       (3)fifo=1用于辅助fifo=0,例如加工过程中需要暂停一会儿去走其他轨迹,可以用fifo=1实现
    rtn = InitCrd(core,crd,fifo);
    if ( 0 != rtn )
    {
        return rtn;
    }

    double endPos[2];                  // 插补数据段目标位置
    double synVel;                     // 插补数据段合成目标速度
    double synAcc;                     // 插补数据段合成加速度
    long segNum;                       // 插补数据段用户段号
    short override2;                   // 插补运动倍率选择标志

    short crdRun;                      // 插补运动状态

    endPos[0] = 0;                     // 初始化x轴插补数据段目标位置为0
    endPos[1] = 0;                     // 初始化y轴插补数据段目标位置为0
    synVel = 500;                      // 插补数据段合成目标速度,单位:mm/s
    synAcc = 50;                       // 插补数据段合成目标加速度,单位:mm/s^2
    segNum = 0;                        // 初始化插补数据段段号为0
    override2 = 0;                     // 插补运动倍率选择标志,0:选择第一个倍率,1:选择第二个倍率

    /*
    optimizeMode:曲线优化模式。
    0:不对轨迹进行处理。
    2:对轨迹进行平滑处理。
    blendingType:blending类型。参数范围:目前只支持类型为0。
    0:误差模式。模式为0时,blendingPrm为误差参数。
    选择指定的轨迹优化模式对轨迹进行优化后,可以对优化后的结果进行blending。
    pad:保留参数,必须为0。
    tolerance:轨迹优化允许误差,单位:mm。
    blendingPrm:blending参数,单位:mm。参数为0表示不进行blending。
    blendingMinAngle:lending的最小角度限制,当轨迹段向量变化量小于该角度时,不进行blending,单位:度,取值范围:[0, 180]。
    blendingMaxAngle:blending的最大角度限制,当轨迹段向量变化量小于该角度时,不进行blending,单位:度,取值范围:[0, 180]。
    */
    TPathOptimizePrm prm;
    memset(&prm, 0, sizeof(TPathOptimizePrm));
    prm.optimizeMode = 2;
    prm.blendingType = 0;
    prm.tolerance = 0.1;
    prm.blendingPrm = 1;
    prm.blendingMinAngle = 30;
    prm.blendingMaxAngle = 150;
    rtn = GTN_SetPathOptimizePrmLa(core, crd, 1, 0, &prm);

    // 压直线插补数据
    rtn += GTN_LnXYEx(core, crd, 0.000, 0.000, synVel,synAcc,segNum++,override2,fifo);
    rtn += GTN_LnXYEx(core, crd, 10.000, 0.000, synVel,synAcc,segNum++,override2,fifo);
    rtn += GTN_LnXYEx(core, crd, 19.659, 2.588, synVel,synAcc,segNum++,override2,fifo);
    rtn += GTN_LnXYEx(core, crd, 26.730, 9.659, synVel,synAcc,segNum++,override2,fifo);
    rtn += GTN_LnXYEx(core, crd, 26.730, 19.659, synVel,synAcc,segNum++,override2,fifo);
    rtn += GTN_LnXYEx(core, crd, 18.070, 24.659, synVel,synAcc,segNum++,override2,fifo);
    rtn += GTN_LnXYEx(core, crd, 10.999, 17.588, synVel,synAcc,segNum++,override2,fifo);
    rtn += GTN_LnXYEx(core, crd, 18.070, 10.517, synVel,synAcc,segNum++,override2,fifo);
    rtn += GTN_LnXYEx(core, crd, 23.070, 19.177, synVel,synAcc,segNum++,override2,fifo);
    rtn += GTN_LnXYEx(core, crd, 13.070, 19.177, synVel,synAcc,segNum++,override2,fifo);
    rtn += GTN_LnXYEx(core, crd, 20.141, 12.106, synVel,synAcc,segNum++,override2,fifo);
    rtn += GTN_LnXYEx(core, crd, 17.553, 21.766, synVel,synAcc,segNum++,override2,fifo);
    rtn += GTN_LnXYEx(core, crd, 17.553, 11.766, synVel,synAcc,segNum++,override2,fifo);
    //测试结果:使用watch工具捕获插补运动的空间位置WATCH_VAR_PRF_POS和合成速度WATCH_VAR_CRD_PRF_VEL可以观察到
    //角度在30~150°以内夹角的线段衔接点,都按用户填写的参数进行了过渡平滑处理,合成速度产生了变化

    if ( 0 != rtn )
    {
        return CommandHandler("GTN_LnXYEx",rtn);
    }

    // 将数据压入运动控制器
    do 
    {
        rtn = GTN_CrdDataEx(core,crd,NULL,fifo);
        if ( 0 == rtn )
        {
            break;
        }
        if ( 1 != rtn )
        {
            return CommandHandler("GTN_CrdDataEx",rtn);
        }
    } while ( 1 == rtn );

    // 启动插补运动
    rtn = GTN_CrdStart(core,1<<(crd-1),fifo<<(crd-1));
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_CrdStart",rtn);
    }
    printf("Start Crd Success !\n");

    do 
    {
        // 运动过程中读取当前状态
        rtn = GetMotionInfo(core,crd,fifo,&crdRun);
        if ( 0 != rtn )
        {
            return rtn;
        }
    } while ( 1 == crdRun );

    // 关闭控制器
    rtn = GTN_Close();
    if ( 0 != rtn )
    {
        return CommandHandler("GTN_Close",rtn);
    }

    printf("Press Any Key To Exit !\n");
    getchar();

    return 0;
}

1.6.3 插补运动指令

1.6.3.1 指令列表

插补运动指令列表

指令 说明
GTN_LnXYEx 缓存区指令,二维直线插补。
GTN_LnXYG0Ex 缓存区指令,二维直线插补,且终点速度始终为0。
GTN_LnXYZEx 缓存区指令,三维直线插补。
GTN_LnXYZG0Ex 缓存区指令,三维直线插补,且终点速度始终为0。
GTN_LnXYZAEx 缓存区指令,四维直线插补。
GTN_LnXYZAG0Ex 缓存区指令,四维直线插补,且终点速度始终为0。
GTN_LnXYZACEx 缓存区指令,五维直线插补。
GTN_LnXYZACG0Ex 缓存区指令,五维直线插补,插补段终点速度为0。
GTN_LnXYZACUVWEx 缓存区指令,八维直线插补。
GTN_LnXYZACUVWG0Ex 缓存区指令,八维直线插补,插补段终点速度为0。
GTN_ArcXYREx 缓存区指令,XY 平面圆弧插补,以终点位置和半径为输入参数。
GTN_ArcYZREx 缓存区指令,YZ平面圆弧插补,以终点位置和半径为输入参数。
GTN_ArcZXREx 缓存区指令,ZX平面圆弧插补,以终点位置和半径为输入参数。
GTN_ArcXYCEx 缓存区指令,XY 平面圆弧插补,以终点位置和圆心为输入参数。
GTN_ArcYZCEx 缓存区指令,YZ平面圆弧插补,以终点位置和圆心为输入参数。
GTN_ArcZXCEx 缓存区指令,ZX 平面圆弧插补,以终点位置和圆心为输入参数。
GTN_ArcXYZEx 缓存区指令,空间圆弧插补。
GTN_HelixXYRZEx 缓存区指令,XY 平面螺旋线插补,以终点位置和半径为输入参数。
GTN_HelixXYCZEx 缓存区指令,XY 平面螺旋线插补,以终点位置和圆心为输入参数。
GTN_EllipseEx 缓存区指令,椭圆插补。
GTN_SetG0Mode 设置插补G0指令模式。
GTN_GetG0Mode 读取插补G0指令模式。

1.6.3.2 重点说明

插补运动在数控机床,切削加工工艺等数控装置中应用广泛。它可以实现多轴的协调运动,将数据段所描述的曲线的起点、终点之间的空间进行数据密化,从而形成要求的轮廓轨迹,根据密化后的数据向各个坐标发出进给脉冲,对应每个脉冲,机床在相应的坐标方向上移动,从而将工件加工出所需要的轮廓形状。

控制器支持的插补模式包括:直线插补、平面圆弧插补、空间圆弧插补、螺旋线插补、椭圆插补等。

1. 直线插补

运动控制器的插补模式支持在2~8维的直线插补。

用户输入直线的终点坐标、速度和加速度等,控制器根据速度规划从起点沿着直线运动到终点。

直线插补示例

假设某数控机床刀具在xy平面从原点出发,走一段如图所示的正六边形轨迹。一共需要走七段轨迹,图中标号已标出。

直线插补示例运动轨迹
例程请参考直线插补运动例程

2. 带G0的直线插补

带G0的指令包括GTN_LnXYG0ExGTN_LnXYZG0ExGTN_LnXYZAG0ExGTN_LnXYZACUVWG0ExGTN_LnXYZACG0Ex。这类运动指令将会完成一个完整的加减速过程,即每段运动的合成速度都是从0开始,结束的时候也是0,一般用于定位。

这类指令与其他插补运动指令的区别在于:如果使用了前瞻预处理功能,调用其他插补运动指令,实际的终点速度是前瞻预处理模块根据用户设置的前瞻预处理参数和运动轨迹计算出来的一个合理的终点速度;但如果调用GTN_LnXYG0ExGTN_LnXYZG0ExGTN_LnXYZACG0ExGTN_LnXYZACUVWG0ExGTN_LnXYZACG0Ex系列指令,终点速度始终是0,控制器不会优化或改变这类指令的终点速度。

带G0的插补指令支持两种定位模式。默认为直线定位模式,即插补中的轴同时启动同时停止,定位轨迹为直线;通过GTN_SetG0Mode可以切换为快速定位模式,即各个轴单独规划,加速度与速度相同,距离短的轴先到位,距离长的轴后到位,定位轨迹可能为折线。

G0直线插补示例

例程请参考G0直线插补运动例程

3. 平面圆弧插补

运动控制器的插补模式支持在XY平面、YZ平面和ZX平面的圆弧插补。

圆弧插补的旋转方向按照右手螺旋定则定义为:从坐标平面的“上方”(即垂直于坐标平面的第三个轴的正方向)看,来确定逆时针方向和顺时针方向。可以这样简单记忆:将右手拇指前伸,其余四指握拳,拇指指向第三个轴的正方向,其余四指的方向即为逆时针方向。映射坐标系为二维坐标系(X-Y)时,XOY坐标平面内的圆弧插补逆时针方向同样定义,如图示。

圆弧插补逆时针方向

圆弧插补有两种描述方法:半径描述方法和圆心坐标描述方法,用户可以根据加工数据选择合适的描述方法来编程。所使用的描述方法遵循G代码的编程标准。

(1) 半径描述方法

调用指令GTN_ArcXYRExGTN_ArcYZRExGTN_ArcZXREx是使用半径描述方法对圆弧进行描述。使用半径描述方法,用户需要输入圆弧终点坐标、圆弧半径、圆弧的旋转方向、速度和加速度等。其中参数半径可为正值,也可为负值,其绝对值为圆弧的半径,正值表示圆弧的旋转角度≤180°,负值表示圆弧的旋转角度>180°,如图所示,半径描述方法无法描述360°的整圆。

半径取正值和负值圆弧插补示意图

(2) 圆心坐标描述方法

调用指令GTN_ArcXYCExGTN_ArcYZCExGTN_ArcZXCEx是使用圆心坐标描述方法对圆弧进行描述。使用圆心描述方法,用户需要输入圆弧终点坐标、圆心相对于起点坐标的相对位置值、圆弧的旋转方向、速度和加速度等。其中,圆心位置值参数的定义如图所示。

圆心坐标描述方法示意图

圆心参数为相对于起点位置的增量值,带正负号,如果起点的坐标为(xStart, yStart),用户设置的圆心参数为(xCenter, yCenter),则圆心坐标值为(xStart+xCenter, yStart+yCenter)。用户设置的起点坐标和终点坐标重合时,则表示将要进行一个整圆的运动。

提示

用户应该保证圆弧描述指令可以正确描述一段圆弧,如果用户所设置的参数不能生成一段正确的圆弧,则指令会返回7(参数错误)。

圆弧插补

假设某数控机床刀具在xy平面走一段如图所示的圆弧。插补运动指令的单位为mm,脉冲当量为1000pulse/mm。使用圆心坐标描述方法描述一个整圆,使用半径描述方法描述一个1/4圆弧,最后回到原点位置。一共需要走三段轨迹,图中用标号标出。整圆的圆心坐标为(100, 0),半径100。圆弧的圆心坐标为原点(0, 0),半径200。

圆弧插补示例运动轨迹
例程请参考圆弧插补运动例程

4. 空间圆弧插补

运动控制器的插补模式支持使用三点描述法描述的空间圆弧插补。通过调用指令GTN_ArcXYZEx,输入空间圆弧的终点坐标、中间点坐标(位于空间圆弧起点和终点之间的某个点)、速度和加速度等,根据起点、中间点和终点确定一个圆弧。

空间圆弧插补示例

假设某数控机床刀具在空间走一段如图所示的空间圆弧。插补运动指令的单位为mm,脉冲当量为1000pulse/mm。从原点位置出发,运动到空间圆弧的起点(100, 0),运动一段空间圆弧,最后回到原点位置。空间圆弧的中间点坐标为(200, -100),终点坐标为(200, 100)。

空间圆弧插补示例运动轨迹(单位pulse)
例程请参考空间圆弧插补运动例程

5. 螺旋线插补

运动控制器的插补模式支持在XY平面、YZ平面和ZX平面的螺旋线插补。

螺旋线插补是平面圆弧插补的扩展,指的是在加工一个平面圆弧的同时,垂直于该平面的直线轴也进行线性插补运动。当圆弧从起点运动终点的同时,直线轴也由起点运动到终点。其中,螺旋线投影的圆弧的描述方式和平面圆弧插补的描述一致,同时输入直线轴的终点位置,即可描述一段螺旋线。

螺旋线插补示例

假设某数控机床刀具在xy平面走一段如图所示的螺旋线。插补运动指令的单位为mm,脉冲当量为1000pulse/mm。从原点位置出发,运动到起点(100, 0, 0),运动一段螺旋线,最后运动到终点(200, 100,200)。螺旋线插补的终点坐标为(200, 100, 100),螺旋线投影的平面圆弧圆心坐标为(200, 0)。

螺旋线插补示例运动轨迹(单位pulse)
例程请参考螺旋线插补运动例程

6. 椭圆插补

运动控制器的插补模式支持在XY平面、YZ平面和ZX平面的椭圆插补。

椭圆插补的旋转方向按照右手螺旋定则定义为:从坐标平面的“上方”(即垂直于坐标平面的第三个轴的正方向)看,来确定逆时针方向和顺时针方向。可以这样简单记忆:将右手拇指前伸,其余四指握拳,拇指指向第三个轴的正方向,其余四指的方向即为逆时针方向。映射坐标系为二维坐标系(X-Y)时,XOY坐标平面内的椭圆插补逆时针方向同样定义,如图示。

椭圆插补逆时针方向

椭圆插补使用辅助点描述模式进行描述,通过调用指令GTN_EllipseEx,用户输入椭圆终点坐标、椭圆所处的平面、椭圆运动方向、椭圆上的5个辅助点坐标、速度和加速度等。

椭圆插补示例

假设某数控机床刀具在xy平面走一段如图所示的椭圆。插补运动指令的单位为mm,脉冲当量为1000pulse/mm。从原点位置出发,运动到起点(-100, 0),逆时针运动一段椭圆,最后回到原点位置。椭圆插补的终点坐标为(0, 50),5个辅助点的坐标分别为(-100, 0) 、(0, -50)、(100, 0)、(0, 50)、(75, 33.0719)。

椭圆插补示例运动轨迹(单位pulse)
例程请参考椭圆插补运动例程

1.6.4 速度倍率

1.6.4.1 指令列表

速度倍率指令列表

指令 说明
GTN_SetOverride 设置插补运动目标合成速度倍率。
GTN_SetOverride2 设置插补运动目标合成速度倍率(第二组)。

1.6.4.2 重点说明

运动控制器支持两组速度倍率,用户可以在插补运动过程中实时调整速度倍率。当调用直线圆弧等插补运动指令时,可以在指令中指定其使用的倍率号,控制器在规划过程中会自动根据当前指令的倍率号使用对应的速度倍率值。

1.6.5 缓存区FIFO的管理

1.6.5.1 重点说明

每个坐标系包含两个缓存区(FIFO):FIFO0和FIFO1,其中FIFO0为主要运动FIFO,FIFO1为辅助运动FIFO,每个FIFO都含有4096段插补数据的空间。

提示

缓存区的释放:用户如果不使用插补模式而直接切换到其他运动模式,插补模式的缓存区就自动释放了,比如调用指令GTN_PrfJog

在运动控制器的插补模式下,不能随意切换到其他运动模式,否则会导致插补坐标系破坏,并且原来压入插补缓存区的数据会丢失。但是在实际应用中,经常会有类似下面例子描述的情况。假如机床在走一段轨迹的途中需要暂停下来,更换路径换刀或者移到安全的地方以查看加工效果,然后再回到暂停时的坐标继续完成剩余的轨迹。为了实现上述操作,应利用每个坐标系提供的两个缓存区FIFO0和FIFO1。

FIFO0是主运动FIFO,用户的主体插补运动的插补数据应该放在FIFO0中。FIFO0的插补运动可以被中断(通过调用GTN_Stop指令),中断后可以进行辅助 FIFO1 的插补运动,辅助 FIFO1 的插补运动完成后,需要将坐标系位置恢复到FIFO0主运动被打断的位置,之后FIFO0 可从断点处继续恢复原来的运动(恢复运动调用GTN_CrdStart指令)。

FIFO1 是辅助运动 FIFO,用户的辅助插补运动的插补数据可以放在 FIFO1 中,FIFO1的插补数据必须在 FIFO0 的运动停止的情况下才能输入,如果 FIFO0 在运动,向 FIFO1 中传递插补数据,将会提示错误。FIFO1 的插补运动也可以暂停、恢复,但是,在FIFO1暂停时不可以进行FIFO0 的插补运动,否则,FIFO1 缓存区将会被清空,无法再恢复 FIFO1的运动,插补主运动与辅助运动流程如图所示。

插补主运动和辅助运动流程
提示

在主运动FIFO0的运动暂停之后,又进行了FIFO1的运动,如果用户希望在FIFO1运动结束之后,继续进行FIFO0的运动,则用户必须保证,FIFO1运动结束后,坐标位置值与FIFO0停止时的坐标位置值(断点位置)相同,否则,FIFO0将不接受启动运动指令,调用GTN_CrdStart指令恢复启动 FIFO0的运动,将会提示错误。

插补FIFO管理示例

假设机床需要做运动:插补运动指令的单位为mm,脉冲当量为1000pulse/mm。主加工运动需要从(0, 0)运动到(100, 100)位置,中途在(50, 50)附近出现了断刀,需要暂停运动去换刀。由于主加工运动已经将预定的轨迹数据压入了缓存区,若重新压入新的数据则会破坏原来的运动,因此需要启动辅助运动来协助完成。
假设换刀轨迹是先走到(70, 30),然后走到(110, 50)位置完成换刀动作。但为了能继续主加工运动,需要回到暂停点,如图所示。

插补FIFO管理之换刀轨迹

1.6.6 插补DMA功能

1.6.6.1 指令列表

插补DMA指令列表

指令 说明
GTN_CrdHsOn 开启插补DMA。
GTN_CrdHsOff 关闭插补DMA。
GTN_GetCrdHsPrm 读取插补坐标系DMA的配置信息。

1.6.6.2 重点说明

插补运动中经常会出现大数据量的连续小线段加工,如果指令传输速率不够,有可能会导致缓冲区跑空,影响加工。为了提高插补指令的传输速率,增加用于高速指令传输的DMA通道。调用 GTN_CrdHsOn开启DMA功能。

1. DMA功能为数据段批量压入运动控制器的功能,可以提高指令的传输速率。每次压入运动控制器的数据段阈值由用户设定,默认为200段。开启DMA后,数据段会先被压入DMA 的缓存区中,当数据段数到达阈值,会自动将缓存区中的指令下发到运动控制器。压完所有的数据段之后,再循环调用GTN_CrdDataEx指令将DMA缓存区中的剩余数据段(剩余的段数未达到阈值)压入运动控制器,直到指令返回值为0。

2. 开启DMA功能后,GTN_CrdSpace查询的为PC端的剩余数据段空间(共有4096段空间)。用户可以根据剩余数据段空间继续压入指令。

3. 插补、振镜和位置比较输出等功能如果需要同时开启DMA功能时,需要选择不同的DMA缓存区序号。

插补DMA示例

例程请参考插补DMA例程

1.6.7 插补运动平滑

1.6.7.1 指令列表

插补运动平滑指令列表

指令 说明
GTN_SetCrdJerkTime 设置插补运动的平滑参数。
GTN_GetCrdJerkTime 读取插补运动的平滑参数。

1.6.7.2 重点说明

控制器默认使用梯形速度规划模式,但在高加速度加工场合,由于加速度过大,很容易激发机台的振动,调用指令GTN_SetCrdJerkTime可以实现高阶的S曲线速度规划,对坐标系运动的合成速度及合成加速度进行平滑,有效抑制机床的振动,并且不会影响轨迹精度。

插补运动平滑功能需要设置平滑时间jerkTime和平滑系数coef。

在相同的加速度情况下,平滑时间越大,运动越平滑,效率越低,反之亦然。一般建议设置为20~50ms。但由于机台的差别性,用户可以根据实际情况在参数允许范围内进行调整。平滑系数越小越平滑,一般建议设置为0。

插补运动平滑示例

轴1轴2运行插补运动,轨迹为一个正方形,没有使用和使用了平滑功能的插补坐标系合成速度和插补轨迹如下所示: 没有使用平滑功能

插补坐标系合成速度和插补轨迹(没有使用平滑功能)

使用了插补运动平滑功能:设置jerkTime=120,coef=1
插补坐标系合成速度和插补轨迹(使用平滑功能)

例程请参考插补平滑例程

1.6.8 手轮引导

1.6.8.1 指令列表

手轮引导功能指令列表

指令 说明
GTN_SetCrdMPGModeEx 设置插补运动的平滑参数。
GTN_GetCrdMPGModeEx 读取手轮导引功能参数。

1.6.8.2 重点说明

在连续轨迹加工中,有时为了在刚刚启动运动时确认加工轨迹是否正确,或者刀具是否已经加工到工件,用户希望可以灵活的控制运动的速度,或者在运动过程中用户希望在暂停后,可以沿着加工过的轨迹回退一段距离,再重新启动运动,基于这些应用,运动控制卡提供了手轮引导插补运动的机制,通过调用GTN_SetCrdMPGModeEx指令进入手轮引导模式,以实现用户的需求。

手轮引导功能是以手轮脉冲发生器作为主轴,手轮速度的快慢决定插补运动的速度,手轮摇动的方向决定插补运动的方向,这样,用户可以通过控制手轮灵活的控制插补运动的方向和速度。在插补缓冲区启动的点位运动也支持手轮引导。

提示

手轮引导在回退时如遇到缓冲区IO指令,则回退停止。

在插补处于运动停止状态时,可以通过调用GTN_SetCrdMPGModeEx进入或退出手轮引导模式,下图是自动加工与手轮引导模式相互切换的状态流程:

自动加工与手轮引导模式相互切换的状态流程
提示

在手轮引导模式下,调用GTN_Stop会直接退出手轮引导模式。

通过GTN_SetCrdMPGModeEx指令设置的masterEven和slaveEven可以设置手轮速度和插补运动速度的比例关系:

插补速度倍率 = 手轮速度*齿轮比
齿轮比 = slaveEven/masterEven
手轮引导

例程请参考手轮引导例程

1.6.9 缓存区辅助指令-IO

1.6.9.1 指令列表

缓冲区辅助IO指令列表

指令 说明
GTN_BufDelayEx 前瞻缓存区指令,前瞻缓存区内延时设置指令。
GTN_BufIOEx 缓存区指令,缓存区内数字量IO输出设置指令。
GTN_BufDoBitEx 前瞻缓存区指令,前瞻缓存区内数字量IO按位输出设置指令。
GTN_BufDAEx 前瞻缓存区指令,前瞻缓存区内输出DA值。

1.6.9.2 重点说明

缓存区内设置延时时间可以通过GTN_BufDelayEx设置,单位为ms。缓存区内设置数字量输出指令为GTN_BufIOExGTN_BufDoBitEx。缓存区内设置模拟量输出指令为GTN_BufDAEx

1.6.10 缓存区辅助单轴运动功能

1.6.10.1 指令列表

缓冲区辅助单轴运动指令列表

指令 说明
GTN_BufGearEx 缓存区指令,启动某个轴跟随运动。
GTN_BufMoveEx 缓存区指令,启动某个轴点位运动。
GTN_BufMoveSpSetPrmEx 缓冲区指令,设置move运动参数。
GTN_BufMoveSpEx 缓存区指令,启动某个轴点位运动(可设置平滑时间)。

1.6.10.2 重点说明

刀向跟随,就是在插补运动的过程中,使非插补轴随着插补运动的合成位移的变化而变化,从而实现在加工过程中,刀具始终处于合适的加工方向和位置的工艺。在本控制卡的插补模块中有两类指令来实现该工艺:缓冲区点位运动和缓冲区跟随运动。

1. 缓冲区点位运动

在插补运动过程中,如果需要坐标系以外的轴进行点位运动,则可以通过在缓存区中压入GTN_BufDAExGTN_BufMoveEx和GTN_BufDAExGTN_BufMoveSpEx指令来实现。GTN_BufDAExGTN_BufMoveEx和GTN_BufDAExGTN_BufMoveSpEx可以在插补运动的过程中插入模态和非模态的点位运动。 模态指令的意义是,在进行该点位运动时,后续的插补缓存区中的指令将会被暂停执行,直到该指令执行完毕后,才执行下一条指令。 非模态指令的意义是,该指令启动了一个轴的点位运动后,立即取下一条缓存区中的指令执行,不会等待点位运动的结束。 GTN_BufMoveEx指令没有平滑功能,如果需要平滑,可以使用GTN_BufMoveSpEx运动指令,该指令除了具有GTN_BufMoveEx指令的功能,还支持设置平滑时间。GTN_BufMoveSpEx使用之前需调用GTN_BufMoveSpSetPrmEx设置运动参数。

提示

需要进行点位运动的轴必须是坐标系外的轴,该轴的运动模式必须是点位运动,且该轴必须处于静止状态,否则该指令不能正常执行。 连续调用缓冲区辅助运动指令:目前支持最多连续调用16条缓冲区辅助运动指令(例如:GTN_BufIOExGTN_BufGearEx……)。

GTN_BufMoveEx示例

假设一个机床加工环境,有一个XY的二维坐标系,和一个不在坐标系内的轴A。运动指令的单位为mm,脉冲当量为1000pulse/mm。
第1段轨迹,刀具先从(0, 0)运动到(200, 200)。
第2段轨迹,在XY方向从(200, 200)运动到(200, 0)的同时,A轴按照一定速度运动到50的位置。这里用到GTN_BufMoveEx的非模态方式。
第3段轨迹,A轴按照一定速度运动到100的位置。等到A轴方向运动完成,在XY坐标内画一个圆心为原点,半径200,位于三四象限的半圆弧(第3段轨迹)。这里用到GTN_BufMoveEx的模态方式。
刀向跟随功能GTN_BufMoveEx的运动轨迹如图所示。

刀向跟随功能GTN_BufMoveEx的运动轨迹

插补合成速度和点位速度如图所示。
插补缓存区内的点位运动速度图

其中蓝色为插补运动的合成速度,红色为点位运动轴的速度值,可以看出第一个点位运动是非模态指令,与插补运动同时运动,而第二个点位运动是模态指令,会阻塞插补运动,只有点位运动结束之后,才能进行插补运动。
例程请参考BufMoveEx例程

提示

在使用插补缓存区中的点位运动功能时,需要注意以下内容:
1. 点位运动的目标位置是相对于机床原点的绝对位置。
2. 如果在上一次的缓存区点位运动没有运动完成,又发送了新的点位运动,则会按照新的点位运动指令进行规划,即可以在插补缓存区中修改点位运动的目标位置和目标速度。
3. 如果在运动过程中停止插补缓存区的运动,则点位运动将不会停止,如果需要停止点位运动,则需要调用GTN_Stop指令停止响应轴的运动。恢复缓存区运动时,用户需自行保证之前点位运动的轴在合适的位置上。
4. 当在模态点位运动的过程中,进行点位运动的轴由于触发限位等异常原因停止时,插补缓存区将不会再继续运行,此时用户需排查异常情况,重新设置相应参数,使系统正常后才可以工作。

2. 缓冲区跟随运动GTN_BufGearEx

在插补过程中,需要坐标系外某一轴跟随坐标系插补运动的时候,可以通过在缓存区中压入GTN_BufGearEx指令来实现,该指令的第二个参数是需要进行跟随运动的轴号。

提示

需要进行跟随运动的轴不能是坐标系中的轴;如果在发送跟随指令GTN_BufGearEx时该轴正在运动,该指令将不能正常执行。

这里需要注意的是,该指令的第三个参数是跟随运动的位移量,该位移量是相对值,即下一段插补段运动过程中跟随轴需要运动的位移量。

提示

GTN_BufMoveExGTN_BufMoveSpEx中的位置参数是相对于机床原点的绝对位置,而GTN_BufGearEx中的位置参数是相对位置。

刀向跟随功能GTN_BufGearEx示例

假设一个机床加工环境,有一个XY的二维坐标系和一个不在坐标系内的轴A。运动指令的单位为mm,脉冲当量为1000pulse/mm。
第1段轨迹,刀具先从(0, 0)运动到(200, 200)。
第2段轨迹,在XY方向从(200, 200)运动到(200, 0)的同时, A轴到50的位置,两者将同时到达各自指定的规划位置。
第3段轨迹,在XY坐标内画一个圆心为原点、半径200、位于三四象限的半圆弧,同时在A轴方向运动100的位置,两者将同时到达各自指定的规划位置。
刀向跟随功能GTN_BufGearEx的运动轨迹如图所示。

刀向跟随功能GTN_BufGearEx的运动轨迹
插补合成速度和点位速度如图所示。
插补缓存区内的跟随运动速度图
例程请参考BufGearEx例程

在使用插补缓存区中的跟随运动功能时,需要注意以下内容:

1. GTN_BufGearEx指令需要在所要跟随的插补段前,不要间隔其他种类的指令,可以同时调用多个GTN_BufGearEx指令来实现多个轴跟随插补运动。

2. 当暂停坐标系运动时,插补的合成速度减速到0,跟随轴的速度也会为0,如果希望重新恢复坐标系运动时,跟随轴仍能够实现跟随,不要调用GTN_Stop指令停止跟随轴的运动,否则重新启动插补缓存区的运动时,跟随轴将无法完成正在进行的跟随运动。

刀向跟随功能——实际工件加工示例

假设机床加工过程中,机床需要加工一个工件,XY平面的尺寸如图所示,工件的厚度为500(单位均为:pulse),采用的工艺是用砂轮磨削工件外轮廓,加工时不但要调整刀具的Z方向高度,同时需要调整砂轮的C向角度(假设砂轮旋转一周的脉冲总数是10000)。在加工工艺中,首先为避免撞到工件,需要将砂轮抬到一定的高度:2000(假设安全高度为2000),从原点空走到A(6000, 6000)位置,之后调整砂轮逆时针转45°(对应为1250 pulse),使之与工件的加工切向方向一致,然后降低砂轮高度到加工位置:-100。接着,沿直线方向加工到B(6000, 12000)位置,从B到C为一圆弧,需要实时更新砂轮的方向,使之保持与工件的加工切向方向一致,所以需要调用GTN_BufGearEx指令,跟随的位移量是2500(90°)。随之后面的加工与之前类似,直至到加工点F(36000, 6000)位置,此时需要抬刀使砂轮改变90°,使之与FA段的加工方向一致,之后再放下砂轮至加工位置,直至加工完工件。

刀向跟随功能之工件尺寸和刀运动轨迹
例程请参考刀向跟随例程

1.6.11 缓存区辅助功能-等待条件

1.6.11.1 指令列表

插补运动中等待条件功能指令列表

指令 说明
GTN_SetLongVar 立即指令,设置内部long型变量的值。
GTN_GetLongVar 立即指令,读取内部long型变量的值。
GTN_BufSetLongVarEx 缓存区指令,插补缓存区中设置内部long型变量的值。
GTN_BufWaitLongVarEx 缓存区指令,设置缓存区等待long型变量。
GTN_GetBufWaitLongVarStatus 缓存区指令,读取缓存区等待变量的状态。
GTN_ClearBufWaitStatus 缓存区指令,清除缓存区等待的状态。

1.6.11.2 重点说明

运动控制器在插补运动中提供了一套等待条件功能,可以实现一些复杂的条件逻辑控制插补运动。该功能支持两种等待条件方式:

1. 立即模式:执行到等待条件指令时,插补运动减速到0,检查等待条件是否满足,如果满足,继续往下执行插补运动,如果不满足,则一直等待,直到超时后停止插补运动。

2. 标识模式:执行到等待条件指令时,插补运动正常规划,并且继续执行下一段插补运动,在下一段插补运动的同时检查等待条件是否满足。如果满足,则继续往下执行插补运动。如果不满足,则在当前段剩余段长大于减速段位移的情况下继续执行插补运动,在当前段剩余位移小于等于减速度位移的情况下减速停止。如果在减速的过程中条件满足,则重新继续插补运动,如果仍然不满足,在超时后停止插补运动。

简言之,立即模式为插补停下来等待,标识模式为在下一段运动过程中一边运动一边等待。