中国自动化学会专家咨询工作委员会指定宣传媒体
文摘详情
gkongbbs

PCIe EtherCAT实时运动控制卡PCIE464的CAD导图与刀向跟随应用

http://www.gkong.com 2025-05-08 14:58 深圳市正运动技术有限公司

一.硬件介绍

PCIE464运动控制卡是正运动推出的一款EtherCAT总线+脉冲型、PCIE接口式的运动控制卡,可选6-64轴运动控制,支持多路高速数字输入输出,可轻松实现多轴同步控制和高速数据传输。

PCIE464运动控制卡.png

PCIE464运动控制卡适合于多轴点位运动、插补运动、轨迹规划、手轮控制、编码器位置检测、IO控制、位置锁存等功能的应用。

PCIE464运动控制卡适用于3C电子加工、检测设备、半导体设备、SMT加工、激光加工、光通讯设备、锂电及光伏设备、以及非标自动化设备等高速高精应用场合。

PCIE464运动控制卡接线.png

PCIE4系列控制卡的应用程序可以使用VC,VB,VS,C++,C#等软件开发,程序运行时需要动态库zmotion.dll,调试时可以将RTSys软件同时连接控制器,从而方便调试、方便观察。

API.png


 

PCIE464产品视频介绍可点击→“【EtherCAT同步周期快至100us】超高实时性PCIe EtherCAT控制卡PCIE464”查看。

更多关于PCIE464的详情介绍,点击“PCIE464 — 高速高精,超高实时性的PCIe EtherCAT实时运动控制卡”查看。

二.接线参考

1.IN数字量输入接口

数字输入分布在J400(IN0-IN7)和X400(IN8-IN39)信号接口中。

IN数字量输入接口.png

2.OUT数字量输出接口

数字输出分布在J400(OUT0-7)和X400(OUT8-OUT39)信号接口中。

OUT数字量输出接口.png

3.单端编码器及单端脉冲接线

单端脉冲接线图.png

单端脉冲接线图

差分脉冲接线图.png

差分脉冲接线图

单端编码器接线图.png

单端编码器接线图

差分编码器接线图.png

差分编码器接线图

注:PCIE464的J400接口中有一个差分脉冲轴接口和三个单端脉冲轴接口,两个差分编码器接口(其中一个与差分脉冲轴接口复用,取决于固件设定)和两个单端编码器接口,具体引脚定义参见PCIE464硬件手册。

三.CAD解析及刀向跟随计算技巧解释

CAD解析.png

1.CAD图纸解析

正运动技术提供开放的ZmotionCadEx库,可导入DXF、Ai、Plt、Dst图纸,可以生成运动坐标数据转G代码、zbasic运动指令、或直接PC函数执行运动。

CAD图纸解析.png

2.刀向跟随计算

刀向跟随,是在插补运动的过程中,使非插补轴随着插补运动的合成位移的变化而变化,从而实现在加工过程中,刀具始终处于合适的加工方向和位置的工艺。

非插补轴(如旋转轴或独立轴)能够根据插补轴的合成位移实时调整,使刀具始终沿预定路径或方向运动。例如,在砂轮磨削中,通过调整砂轮的角度,确保其与工件切向方向一致;在布料裁切中,通过调整刀具朝向,始终指向行进方向。

本例中通过计算小线段的向量方向,以3轴插补形式运行实现,基本原理如下图所示。

刀向跟随计算.png

已知线段起点A坐标,终点B坐标,在起点A上做与X轴平行向量AC,通过数学计算得到向量AB与向量AC的夹角α,即线段AB与X轴正方向夹角。将夹角α与终点坐标XY做三轴插补来作为刀向跟随。

在某些场合需要A轴先抬刀转到加工角度,再下刀加工:比如加工矩形轮廓四条边,那么应该先抬刀转到加工方向,再下刀进行加工,不应在工件上转刀,此时可以通过判断线段的长度,小于设定值时三轴插补,大于设定值时应抬刀→转刀→下刀再加工。

本例中主要用到了下面几个函数接口:

四.MFC与C++编程实现

1.首先打开Visual Studio 2022,点击创建新项目。

创建新项目.png

2.选择开发语言为“Visual C++”和程序类型“MFC应用程序”。

选择开发语言.png

3.点击下一步即可。

1.png

4.选择类型为“基于对话框”,下一步或者完成。

2.png

5.前往正运动官网下载PC函数库,路径如下(本文采用64位函数库为例)。

(1)进入官网,选择支持与服务,打开下载中心选择库文件,就能找到所有的PC函数库。

PC函数库.png

(2)点击下载Windows C++(64位),可按需求另存为想要保存的路径下。

下载.png

(3)函数库另存为具体路径如下。

函数库另存为.png

(4)如需处理CAD图,可与技术工程师联系获取ZmotionCadEx库。

3.png

6.将厂商提供的C++库文件和相关头文件复制到新建的项目里。

4.png

7.在项目中添加静态库和相关头文件。

(1)打开解决方案资源管理器,点击显示全部文件。

5.png

(2)选中.h与.lib文件,右键包括在项目中。

6.png

8.声明用到的头文件。

7.png

9.至此项目新建完成,可进行MFC项目开发。

五.查看PC函数手册,熟悉相关函数接口

1.PC函数手册也可以在正运动官网“支持与服务”→“下载中心”→“编程手册”中找到。

PC函数手册.png

2.链接控制器,获取链接句柄。

PC指令.png

3.查看ZmotionCadEx.h中关于CAD图处理的函数接口。

CAD图处理函数接口.png

六.MFC实现CAD导入与刀向跟随

1.例程界面如下。

MFC实现CAD导入与刀向跟随.png

2.初始化连接到控制器。

BOOL CZCadToolFollowDemoDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    // 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
    //  执行此操作
    SetIcon(m_hIcon, TRUE);     // 设置大图标
    SetIcon(m_hIcon, FALSE);    // 设置小图标
    // TODO: 在此添加额外的初始化代码
    m_DlgRect.SetRect(0, 0, 0, 0);//初始化对话框大小存储变量
    CRect rect;
    GetClientRect(&rect); //取客户区大小
    Old.x = rect.right - rect.left;
    Old.y = rect.bottom - rect.top;
    GetClientRect(&m_DlgRect);
    GetDlgItem(IDC_PIC_VIEW)->GetClientRect(m_rcView);
    /*--------------------控制器连接--------------------*/
    char str_ip[] = "192.168.0.11";
    int m_liv_ZmcMoveRe = ZMC_SearchAndOpenEth(str_ip, 800, &g_handle);
    if (ERR_OK != m_liv_ZmcMoveRe)
    {
        if (MessageBox((L"控制器连接失败,是否打开仿真版运行?"), (L"温馨提示"), MB_YESNO | MB_ICONQUESTION) == IDYES)
        {
            if (!IsProccessRunning(_T("ZSimu.exe")))
            {
                ShellExecute(NULL, L"open", _T("\\仿真器\\ZSimu.exe"), NULL, NULL, SW_SHOWNORMAL);
            }
            else
            {
                HANDLE hProcessHandle;
                ULONG nProcessID;
                HWND TheWindow;
                TheWindow = ::FindWindow(NULL, _T("ZSimu.exe"));
                ::GetWindowThreadProcessId(TheWindow, &nProcessID);
                hProcessHandle = ::OpenProcess(PROCESS_TERMINATE, FALSE, nProcessID);
                ::TerminateProcess(hProcessHandle, 4);
                ShowWindow(nProcessID);
            }
            strcpy_s(str_ip, "127.0.0.1");
            m_liv_ZmcMoveRe = ZAux_OpenEth(str_ip, &g_handle);
            if (m_liv_ZmcMoveRe != ERR_OK)
            {
                Sleep(1000);
                m_liv_ZmcMoveRe = ZAux_OpenEth(str_ip, &g_handle);
                if (m_liv_ZmcMoveRe != ERR_OK)
                {
                    Sleep(1000);
                    m_liv_ZmcMoveRe = ZAux_OpenEth(str_ip, &g_handle);
                    if (m_liv_ZmcMoveRe != ERR_OK)
                    {
                        Sleep(1000);
                        m_liv_ZmcMoveRe = ZAux_OpenEth(str_ip, &g_handle);
                        if (m_liv_ZmcMoveRe != ERR_OK)
                        {
                            MessageBox(L"仿真器连接失败(请给予仿真器权限)");
                        }
                    }
                }
            }
        }
    }
    if (NULL != g_handle)
    {
        SetTimer(0, 1, NULL);
        //初始化轴参数
        for (int i = 0; i <=3; i++)
        {
            ZAux_Direct_SetAtype(g_handle, i, 0);//轴类型  虚拟轴
            ZAux_Direct_SetUnits(g_handle, i, 10000);//脉冲当量 1000 脉冲为单位
            ZAux_Direct_SetSpeed(g_handle, i, 100);//速度UNITS / S
            ZAux_Direct_SetAccel(g_handle, i, 1000);//加速度
            ZAux_Direct_SetDecel(g_handle, i, 1000);//减速度
            ZAux_Direct_SetSpeedRatio(g_handle, i, 1);//速度比例
        }
        m_strSpeedRatio.Format(L"速度:%0.2f%%", 1 * 100.0);
        UpdateData(false);
    }
    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}



3.定时器获取轴DPOS坐标用于显示与刷新绘制图像。

void CZCadToolFollowDemoDlg::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    ZAux_GetModbusDpos(g_handle, 3, gfarr_ListDpos);
    if (gfarr_ListDposLast[0] != gfarr_ListDpos[0] || gfarr_ListDposLast[1] != gfarr_ListDpos[1] || gfarr_ListDposLast[2] != gfarr_ListDpos[2] )
    {
        GetDlgItem(IDC_EDIT_DPOS_X)->SetWindowText(L"X:" + FloatToCString(gfarr_ListDpos[0]));
        GetDlgItem(IDC_EDIT_DPOS_Y)->SetWindowText(L"Y:" + FloatToCString(gfarr_ListDpos[1]));
        GetDlgItem(IDC_EDIT_DPOS_A)->SetWindowText(L"A:" + FloatToCString(gfarr_ListDpos[2]));
        gfarr_ListDposLast[0] = gfarr_ListDpos[0];
        gfarr_ListDposLast[1] = gfarr_ListDpos[1];
        gfarr_ListDposLast[2] = gfarr_ListDpos[2];
        InvalidateRect(m_rcView,false);//刷新绘图
    }
    CDialogEx::OnTimer(nIDEvent);
}


4.消息传递函数中响应手动运动按钮代码。

BOOL CZCadToolFollowDemoDlg::PreTranslateMessage(MSG* pMsg)
{
    // TODO: 在此添加专用代码和/或调用基类
    //MFC界面按钮检测
    if (pMsg->message == WM_LBUTTONDOWN)
    {
        if (pMsg->hwnd == GetDlgItem(IDC_BTN_X_A)->m_hWnd) { ZAux_Direct_Single_Vmove(g_handle, 0, 1); }
        if (pMsg->hwnd == GetDlgItem(IDC_BTN_X_B)->m_hWnd) { ZAux_Direct_Single_Vmove(g_handle, 0, -1); }
        if (pMsg->hwnd == GetDlgItem(IDC_BTN_Y_A)->m_hWnd) { ZAux_Direct_Single_Vmove(g_handle, 1, 1); }
        if (pMsg->hwnd == GetDlgItem(IDC_BTN_Y_B)->m_hWnd) { ZAux_Direct_Single_Vmove(g_handle, 1, -1); }
        if (pMsg->hwnd == GetDlgItem(IDC_BTN_A_A)->m_hWnd) { ZAux_Direct_Single_Vmove(g_handle, 2, 1); }
        if (pMsg->hwnd == GetDlgItem(IDC_BTN_A_B)->m_hWnd) { ZAux_Direct_Single_Vmove(g_handle, 2, -1); }
    }
    else if (pMsg->message == WM_LBUTTONUP || WM_LBUTTONDBLCLK == pMsg->message)//判断是否有MFC界面按钮弹起
    {
        if (pMsg->hwnd == GetDlgItem(IDC_BTN_X_A)->m_hWnd) { ZAux_Direct_Single_Cancel(g_handle, 0, 2); }
        if (pMsg->hwnd == GetDlgItem(IDC_BTN_X_B)->m_hWnd) { ZAux_Direct_Single_Cancel(g_handle, 0, 2); }
        if (pMsg->hwnd == GetDlgItem(IDC_BTN_Y_A)->m_hWnd) { ZAux_Direct_Single_Cancel(g_handle, 1, 2); }
        if (pMsg->hwnd == GetDlgItem(IDC_BTN_Y_B)->m_hWnd) { ZAux_Direct_Single_Cancel(g_handle, 1, 2); }
        if (pMsg->hwnd == GetDlgItem(IDC_BTN_A_A)->m_hWnd) { ZAux_Direct_Single_Cancel(g_handle, 2, 2); }
        if (pMsg->hwnd == GetDlgItem(IDC_BTN_A_B)->m_hWnd) { ZAux_Direct_Single_Cancel(g_handle, 2, 2); }
    }
    return CDialogEx::PreTranslateMessage(pMsg);
}




5.绘图函数。

void CZCadToolFollowDemoDlg::OnDraw(CDC *pDC)
{
    CDC dcMem;
    CBitmap bmp;
    GetDlgItem(IDC_PIC_VIEW)->GetClientRect(m_rcView);
    dcMem.CreateCompatibleDC(pDC);
    bmp.CreateCompatibleBitmap(pDC, m_rcView.Width(), m_rcView.Height());
    dcMem.SelectObject(&bmp);
    //绘制背景,默认黑色背景
    dcMem.FillSolidRect(m_rcView, RGB(0, 0, 0));
    //显示当前加载的文件名
    SetTextColor(dcMem.m_hDC, RGB(125, 125, 125));
    TextOut(dcMem.m_hDC, 5, 5, m_strCurFileName, m_strCurFileName.GetLength());
    //实际显示的区域,预留边距(不完全撑满)
    double WinWidth = m_rcView.Width() - 65;
    double WinHeight = m_rcView.Height() - 65;
    // 实际的区域
    double ObjectPixWidth = 0.0, ObjectPixHeight = 0.0;
    //需要更新初始化显示比例
    if (!m_bInitZoomTran)
    {
        //根据视图范围、图形范围,计算显示比例及偏移
        if ((NULL != m_pVectGroup) || (NULL != m_pDib))
        {
            float fLeft = 0.0, fBottom = 0.0, fWidth = 0.0, fHeight = 0.0;
            double dScale = 0.0;
            //显示图形还是位图,同时只显示一种
            Struct_ZCad_Item *pShowData = m_pVectGroup ? (Struct_ZCad_Item*)m_pVectGroup : (Struct_ZCad_Item*)m_pDib;
            //获取当前图形范围
            ZMotionCad3_GetRange((Struct_ZCad_Item*)pShowData, &fLeft, &fBottom, &fWidth, &fHeight, 0.05);
            if (fWidth < 0.0001 && fHeight < 0.0001)
            {
                fLeft = 0.0;
                fBottom = 0.0;
                fWidth = 100.0;
                fHeight = 100.0;
            }
            //根据图形范围、显示视图范围计算显示比例
            if (fabs(fWidth)  0.0001)
            {
                ObjectPixHeight = WinHeight;
                ObjectPixWidth = ObjectPixHeight * fWidth / fHeight;
                dScale = ObjectPixHeight / fHeight;
            }
            else if (fabs(fWidth) > 0.0001 && fabs(fHeight) <= 0.0001)
            {
                ObjectPixWidth = WinWidth;
                ObjectPixHeight = ObjectPixWidth * fHeight / fWidth;
                dScale = ObjectPixWidth / fWidth;
            }
            else
            {
                if (fWidth*WinHeight BitBlt(m_rcView.left, m_rcView.top, m_rcView.Width(), m_rcView.Height(), &dcMem, 0, 0, SRCCOPY);
    DeleteObject(dcMem); //清理资源
    DeleteObject(bmp);
}


6.打开CAD图处理函数。

//打开CAD文件
void CZCadToolFollowDemoDlg::OnBnClickedBtnOpenCad()
{
    CString gReadFilePathName;
    CFileDialog fileDlg(true, _T("dxf"), _T("*.dxf"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("DXF Files (*.dxf)|*.dxf|AI File(*.ai)|*.ai|PLT File(*.plt)|*.plt|DST Files (*.dst)|*.dst|All Files (*.*)|*.*||"), NULL);
    if (fileDlg.DoModal() == IDOK)    //弹出对话框
    {
        gReadFilePathName = fileDlg.GetPathName();//得到完整的文件名和目录名拓展名
        CString filename = fileDlg.GetFileName();
        // 取得扩展名
        CString strExtName = filename.Mid(filename.ReverseFind(_T('.')) + 1); //文件扩张名
        strExtName.MakeLower();
        //清除之前加载的
        if (NULL != m_pVectGroup)
        {
            ZMotionCad3_ItemDelete((Struct_ZCad_Item*)m_pVectGroup);
            m_pVectGroup = NULL;
        }
        if (NULL != m_pDib)
        {
            ZMotionCad3_ItemDelete((Struct_ZCad_Item*)m_pDib);
            m_pDib = NULL;
        }
        //加载文件
        if ((strExtName == "dxf") || (strExtName == "ai") || (strExtName == "dst") || (strExtName == "plt"))
        {
            //图形文件
            m_pVectGroup = ZMotionCad3_ImportVectGraph(CW2A(gReadFilePathName), 1016.0, ZCAD_IMPORTVECT_SEGES, 0.05, false);
        }
        else
        {
            //其他格式按位图文件处理
            m_pDib = ZMotionCad3_ImportImage(CW2A(gReadFilePathName), ZCAD_IMPORT_IMAGE_OPTION_NONE);
        }
        //CAD图数据转G代码并生成运动数据链表
        CString m_strGcode = VectToNcCode(m_pVectGroup);
        //打印运动数据到输出窗口
        CncLinkedList_Printf(m_pCncData);
        //刷新G代码显示窗口
        GetDlgItem(IDC_EDIT_GCODE)->SetWindowText(m_strGcode);
        //刷新视图
        InvalidateRect(m_rcView, false);
        //设置刷新状态
        m_bInitZoomTran = false;
        //更新当前文件名
        m_strCurFileName = filename;
    }
}


7.CAD图层数据转G代码函数。

//图形转G代码
CString CZCadToolFollowDemoDlg::VectToNcCode(Struct_ZCad_VectGroup *pVectGroup)
{
    if (!pVectGroup)
    { //图形数据为空
        AfxMessageBox(L"数据为空!");
        return L"";
    }
    CncLinkedList_ClearMyList(m_pCncData);
    m_pCncData=CncLinkedList_CreatList();
    CString strOut, strSingleLen; //输出的G代码
    strOut.Format(L"M3\r\nG90\r\nG0 X0.000 Y0.000\r\n"); //默认绝对运行
    CncLinkedList_StringToLink(L"G0 X0.000 Y0.000",m_pCncData);//G代码字符串转入链表中
    Struct_ZCad_VectGroup *pGroupHead = pVectGroup;
    while (pGroupHead)
    {
        Struct_ZCad_Vect *pVectHead = pGroupHead->m_pVect;
        Struct_ZCad_Seg *pEffectSeg = NULL; //有效数据
        while (pVectHead)
        {
            if (ZCAD_ITEMTYPE_VECTLine != pVectHead->m_itemtype && !pVectHead->m_pLineSeg)
            { //曲线全部转为小线段处理
                ZMotionCadEx_CurveSmooth((Struct_ZCad_Item *)pVectHead, 0.05, false, true);
            }
            pEffectSeg = pVectHead->m_pLineSeg ? pVectHead->m_pLineSeg : pVectHead->m_pLine;
            //快速定位
            strSingleLen.Format(L"G0 X%.4lf Y%.4lf\r\n", pEffectSeg->x1, pEffectSeg->y1);
            CncLinkedList_StringToLink(strSingleLen, m_pCncData);
            strOut += strSingleLen;
            if (ZCAD_ITEMTYPE_VECTArc == pVectHead->m_itemtype)
            { //圆/圆弧的情况下//不会进入此if,全部转小线段处理
                CString strSide; //圆的方向
                double dX0 = min(pVectHead->m_pLine->x1, pVectHead->m_pLine->x2) + fabs(pVectHead->m_pLine->x1 - pVectHead->m_pLine->x2) / 2;
                double dY0 = min(pVectHead->m_pLine->y1, pVectHead->m_pLine->y2) + fabs(pVectHead->m_pLine->y1 - pVectHead->m_pLine->y2) / 2; //圆心的绝对坐标
                double dR = fabs(pVectHead->m_pLine->x1 - pVectHead->m_pLine->x2) / 2.0; //半径
                if (pVectHead->m_pLine->x1 m_pLine->x2 && pVectHead->m_pLine->y1 m_pLine->y2)
                { //逆时针G03
                    strSide = "G3";
                }
                else
                { //顺时针G02
                    strSide = "G2";
                }
                strSingleLen.Format(L"%s X%.4lf Y%.4lf I%.4lf J%.4lf\r\n", strSide, pEffectSeg->m_prev->x2, pEffectSeg->m_prev->y2, dX0 - pEffectSeg->x1, dY0 - pEffectSeg->y1);
                CncLinkedList_StringToLink(strSingleLen, m_pCncData);
                strOut += strSingleLen;
            }
            else
            {
                Struct_ZCad_Seg *pHeadSeg = pEffectSeg;
                while (pHeadSeg)
                { //直线插补
                    strSingleLen.Format(L"G1 X%.4lf Y%.4lf\r\n", pHeadSeg->x2, pHeadSeg->y2);
                    CncLinkedList_StringToLink(strSingleLen, m_pCncData);
                    strOut += strSingleLen;
                    pHeadSeg = pHeadSeg->m_pnext;
                    if (pHeadSeg == pEffectSeg)
                    {
                        break;
                    }
                }
            }
            pVectHead = pVectHead->m_pnext;
            if (pVectHead == pGroupHead->m_pVect)
            {
                break;
            }
        }
        pGroupHead = pGroupHead->m_pnext;
        if (pGroupHead == pVectGroup)
        {
            break;
        }
    }
    strSingleLen.Format(L"M2");
    CncLinkedList_StringToLink(strSingleLen, m_pCncData);
    strOut += strSingleLen;
    return strOut;
}



8.计算直线向量角度函数。

//第一条直线起点,终点;第二条直线起点终点
double GetLineVectorArg(double line1StartX, double line1StartY, double line1EndX, double line1EndY)
{
    float a[4];// 存放第一个向量的起点和重点
    float b[4];// 存放第二个向量的起点和重点
    a[0] = line1StartX;
    a[1] = line1StartY;
    a[2] = line1EndX;
    a[3] = line1EndY;
    if (a[0] == a[2] && a[1] == a[3])
    {
        return 0;
    }
    b[0] = 0;
    b[1] = 0;
    b[2] = 1000;
    b[3] = 0;
    float vector1x = a[0] - a[2];
    float vector1y = a[1] - a[3];
    float vector2x = b[0] - b[2];
    float vector2y = b[1] - b[3];
    CString  TRACEtest;
    float ang = 0;
    //θ=acos(v1⋅v2/||v1||||v2||)
    float tmp = ((vector1x)*(vector2x)+(vector1y)*(vector2y)) / (sqrt(pow(vector1x, 2) + pow(vector1y, 2))*sqrt(pow(vector2x, 2) + pow(vector2y, 2)));
    //θ=atan2(v2.y,v2.x)−atan2(v1.y,v1.x)    double tmp2 = (atan2(vector2y, vector2x) - atan2(vector1y, vector1x))*(180 / PI);    ang = acos(tmp)*(180 / PI);
    if (int(ang) == 90)
        if (line1StartY < line1EndY) {
            ang = 90;
        }
        else {
            ang = 270;
        }
    if (fabs(ang - tmp2) <= 1e-3)
    {
        ang = 360 - ang;
    }
    return ang;
}


9.G代码字符串转数据链表函数。

void CZCadToolFollowDemoDlg::CncLinkedList_StringToLink(CString FileContentLine, struct_CncData * CncData)
{
    FileContentLine.TrimRight();//去掉右边的空格
    FileContentLine.TrimLeft();//去掉左边的空格
    FileContentLine.Replace(_T(" "), _T(""));
    struct_MoveData movepara;
    CncLinkedList_InsertNodebyTail(CncData, movepara);
    //找到最后一个节点
    struct_CncData *cncData_lastNode = CncData;
    while (cncData_lastNode->m_pnext != CncData)
    {
        cncData_lastNode = cncData_lastNode->m_pnext;//往下走
     }
    if (FileContentLine.Find('G') != -1)
    {
        if (cncData_lastNode->m_pprev != NULL)
        {
            cncData_lastNode->m_movepara.m_length = cncData_lastNode->m_pprev->m_movepara.m_length + 1;
        }
        CString str_tmp = FileContentLine.Right(FileContentLine.GetLength() - FileContentLine.Find('G') - 1);//剩余G后字符串
        int num_tmp = _tstof(str_tmp);
        CString str_tmp90, str_tmpX, str_tmpY, str_tmpZ, str_tmpI, str_tmpJ, str_tmpD;
        AfxExtractSubString(str_tmpX, (LPCTSTR)FileContentLine, 1, 'X');
        AfxExtractSubString(str_tmpY, (LPCTSTR)FileContentLine, 1, 'Y');
        AfxExtractSubString(str_tmpI, (LPCTSTR)FileContentLine, 1, 'I');
        AfxExtractSubString(str_tmpJ, (LPCTSTR)FileContentLine, 1, 'J');
        AfxExtractSubString(str_tmpD, (LPCTSTR)FileContentLine, 1, 'D');
        /****************************G运动类型赋值*******************************************/
        switch (num_tmp)
        {
            case CNC_TRACK_LINE_SEEK://0定位
                cncData_lastNode->m_movepara.m_type = CNC_TRACK_LINE_SEEK;
                break;
            case CNC_TRACK_LINE://1直线
                cncData_lastNode->m_movepara.m_type = CNC_TRACK_LINE;
                break;
            case CNC_TRACK_ARC://2圆弧
                cncData_lastNode->m_movepara.m_type = CNC_TRACK_ARC;
                break;
            case CNC_TRACK_ARC_CCW://3圆弧
                cncData_lastNode->m_movepara.m_type = CNC_TRACK_ARC_CCW;
                break;
            default:
                cncData_lastNode->m_movepara.m_type = CNC_TRACK_OTHER;
                break;
        }
        /****************************G运动坐标赋值*******************************************/
        {
            if (str_tmpX.GetLength() > 0)
            {
                cncData_lastNode->m_movepara.m_end.x = _tstof(str_tmpX);
            }
            else
            {
                cncData_lastNode->m_movepara.m_end.x = CNC_XYZIJ_NULL;
            }
            if (str_tmpY.GetLength() > 0)
            {
                cncData_lastNode->m_movepara.m_end.y = _tstof(str_tmpY);
            }
            else
            {
                cncData_lastNode->m_movepara.m_end.y = CNC_XYZIJ_NULL;
            }
            if (str_tmpI.GetLength() > 0)
            {
                cncData_lastNode->m_movepara.m_center.x = _tstof(str_tmpI);
            }
            else
            {
                cncData_lastNode->m_movepara.m_center.x = CNC_XYZIJ_NULL;
            }
            if (str_tmpJ.GetLength() > 0)
            {
                cncData_lastNode->m_movepara.m_center.y = _tstof(str_tmpJ);
            }
            else
            {
                cncData_lastNode->m_movepara.m_center.y = CNC_XYZIJ_NULL;
            }
        }
        //获取起点坐标
        float startx, starty, endx, endy;
        startx = CNC_XYZIJ_NULL;
        starty = CNC_XYZIJ_NULL;
        endx = cncData_lastNode->m_movepara.m_end.x;
        endy = cncData_lastNode->m_movepara.m_end.y;
        if (CNC_XYZIJ_NULL == endx && CNC_XYZIJ_NULL == endy)
        {
            return;
        }
        if (CNC_TRACK_LINE == cncData_lastNode->m_movepara.m_type || CNC_TRACK_ARC == cncData_lastNode->m_movepara.m_type || CNC_TRACK_ARC_CCW == cncData_lastNode->m_movepara.m_type)
        {
            struct_CncData *cncData_lastNode_prev = cncData_lastNode;
            int liv_type = -1;
            while (true)
            {
                cncData_lastNode_prev = cncData_lastNode_prev->m_pprev;
                liv_type = cncData_lastNode_prev->m_movepara.m_type;
                if ((0 == liv_type || 1 == liv_type || 2 == liv_type || 3 == liv_type))
                {
                    break;
                }
            }
            startx = cncData_lastNode_prev->m_movepara.m_end.x;
            starty = cncData_lastNode_prev->m_movepara.m_end.y;
            if (startx != CNC_XYZIJ_NULL && startx != CNC_XYZIJ_NULL)
            {
                cncData_lastNode->m_movepara.m_start.x = startx;
                cncData_lastNode->m_movepara.m_start.y = starty;
            }
            else
            {
                AfxMessageBox(L"cncData_lastNode->m_movepara.m_start.x = CNC_XYZIJ_NULL;");
                cncData_lastNode->m_movepara.m_start.x = CNC_XYZIJ_NULL;
                cncData_lastNode->m_movepara.m_start.y = CNC_XYZIJ_NULL;
            }
        }
        switch (num_tmp)
        {
            case CNC_TRACK_LINE_SEEK: //0定位
                if (cncData_lastNode->m_pprev != NULL)
                {
                    cncData_lastNode->m_movepara.m_length = cncData_lastNode->m_pprev->m_movepara.m_length + 1;
                }
                cncData_lastNode->m_movepara.m_type = CNC_TRACK_LINE_SEEK;
                break;
            case CNC_TRACK_LINE://1直线
            {
                if (cncData_lastNode->m_pprev != NULL)
                {
                    cncData_lastNode->m_movepara.m_length = cncData_lastNode->m_pprev->m_movepara.m_length + 1;
                }
                cncData_lastNode->m_movepara.m_type = CNC_TRACK_LINE;
                cncData_lastNode->m_movepara.m_angleStart = GetLineVectorArg(startx, starty, endx, endy);
                cncData_lastNode->m_movepara.m_angleEnd = GetLineVectorArg(startx, starty, endx, endy);
                break;
            }
            case CNC_TRACK_ARC://2圆弧
            {
                if (cncData_lastNode->m_pprev != NULL)
                {
                    cncData_lastNode->m_movepara.m_length = cncData_lastNode->m_pprev->m_movepara.m_length + 1;
                }
                cncData_lastNode->m_movepara.m_type = CNC_TRACK_ARC;
                float m_c_x = startx + cncData_lastNode->m_movepara.m_center.x;//圆心绝对坐标
                float m_c_y = starty + cncData_lastNode->m_movepara.m_center.y;
                cncData_lastNode->m_movepara.m_angleStart = GetCircVectorArgG02(startx, starty, m_c_x, m_c_y);
                cncData_lastNode->m_movepara.m_angleEnd = GetCircVectorArgG02(cncData_lastNode->m_movepara.m_end.x, cncData_lastNode->m_movepara.m_end.y, m_c_x, m_c_y);
                if (cncData_lastNode->m_movepara.m_angleStart == cncData_lastNode->m_movepara.m_angleEnd)
                {
                    cncData_lastNode->m_movepara.m_angleEnd += 360;
                }
                if (cncData_lastNode->m_movepara.m_angleEnd - cncData_lastNode->m_movepara.m_angleStart > 180)
                {
                    cncData_lastNode->m_movepara.m_angleEnd -= 360;
                }
                break;
            }
            case CNC_TRACK_ARC_CCW://3圆弧
            {
                if (cncData_lastNode->m_pprev != NULL)
                {
                    cncData_lastNode->m_movepara.m_length = cncData_lastNode->m_pprev->m_movepara.m_length + 1;
                }
                cncData_lastNode->m_movepara.m_type = CNC_TRACK_ARC_CCW;
                float m_c_x = startx + cncData_lastNode->m_movepara.m_center.x;//圆心绝对坐标
                float m_c_y = starty + cncData_lastNode->m_movepara.m_center.y;
                cncData_lastNode->m_movepara.m_angleStart = GetCircVectorArgG03(startx, starty, m_c_x, m_c_y);
                cncData_lastNode->m_movepara.m_angleEnd = GetCircVectorArgG03(cncData_lastNode->m_movepara.m_end.x, cncData_lastNode->m_movepara.m_end.y, m_c_x, m_c_y);
                if (cncData_lastNode->m_movepara.m_angleStart == cncData_lastNode->m_movepara.m_angleEnd)
                {
                    cncData_lastNode->m_movepara.m_angleEnd += 360;
                }
                break;
            }
            default:
                if (cncData_lastNode->m_pprev != NULL)
                {
                    cncData_lastNode->m_movepara.m_length = cncData_lastNode->m_pprev->m_movepara.m_length + 1;
                }
                cncData_lastNode->m_movepara.m_type = CNC_TRACK_OTHER;
                break;
            }
        }
     else//M
     {
        if (cncData_lastNode->m_pprev != NULL)
        {
            cncData_lastNode->m_movepara.m_length = cncData_lastNode->m_pprev->m_movepara.m_length + 1;
        }
        CString str_tmp = FileContentLine.Right(FileContentLine.GetLength() - FileContentLine.Find('M') - 1);//剩余M后字符串
        int num_tmp = _tstof(str_tmp);
        switch (num_tmp)
        {
            case 3:   //开始切割
                if (cncData_lastNode->m_pprev != NULL)
                {
                    cncData_lastNode->m_movepara.m_length = cncData_lastNode->m_pprev->m_movepara.m_length + 1;
                }
                cncData_lastNode->m_movepara.m_type = CNC_TRACK_START;
                break;
            case 9:  //下刀
                if (cncData_lastNode->m_pprev != NULL)
                {
                    cncData_lastNode->m_movepara.m_length = cncData_lastNode->m_pprev->m_movepara.m_length + 1;
                }
                cncData_lastNode->m_movepara.m_type = CNC_TRACK_TOOL_DOWN;
                break;
            case 10: //抬刀
                if (cncData_lastNode->m_pprev != NULL)
                {
                    cncData_lastNode->m_movepara.m_length = cncData_lastNode->m_pprev->m_movepara.m_length + 1;
                }
                cncData_lastNode->m_movepara.m_type = CNC_TRACK_TOOL_UP;
                break;
            case 4:   //切割结束
            case 2:
                if (cncData_lastNode->m_pprev != NULL)
                {
                    cncData_lastNode->m_movepara.m_length = cncData_lastNode->m_pprev->m_movepara.m_length + 1;
                }
                cncData_lastNode->m_movepara.m_type = 4;
                break;
            case 30: //M代码(固定)
                if (cncData_lastNode->m_pprev != NULL)
                {
                    cncData_lastNode->m_movepara.m_length = cncData_lastNode->m_pprev->m_movepara.m_length + 1;
                }
                cncData_lastNode->m_movepara.m_type = 24;
                break;
            default:
                break;
            }
      }
      //free(cncData_lastNode);
}



10.运动线程函数。

int CZCadToolFollowDemoDlg::CncLinkedList_Move(struct_CncData * List)
{
    if (NULL == List)
    {
        return-1;
    }
    struct_CncData *ptr = List->m_pnext;
    CString m_ShowData = L"";
    float fX = 0.0f, fY = 0.0f, fC = 0.0f, fI = 0.0f, fJ = 0.0f;
    int axislist[3] = { 0,1,2 };  //运动轴列表,其中轴1为主轴
    float circ_endc = 0.0f, circ_stratc = 0.0f;
    float poslist[3] = { 0.0f };
    int m_liv_ZmcMoveRe = 0;
    float lfv_SpeedG00 = 1000;
    float lfv_SpeedG01 = 300;
    ZAux_Direct_SetForceSpeed(g_handle, 0, lfv_SpeedG00);//设置G00定位速度
    ZAux_Direct_SetSpeed(g_handle, 0, lfv_SpeedG01);//设置G01直线速度
    while (ptr != List) {
        fX = ptr->m_movepara.m_end.x;
        fY = ptr->m_movepara.m_end.y;
        fC = ptr->m_movepara.m_angleEnd;
        switch (ptr->m_movepara.m_type)
        {
            case CNC_TRACK_START://切割开始
            {
                break;
            }
            case CNC_TRACK_LINE_SEEK://0定位
            {
                m_ShowData.Format(_T("G00 X%f Y%f \r\n"), fX, fY);
                TRACE(m_ShowData);
                struct_CncData *cncData_lastNode_next = ptr;
                int liv_type = -1;
                while (true)
                {
                    cncData_lastNode_next = cncData_lastNode_next->m_pnext;
                    liv_type = cncData_lastNode_next->m_movepara.m_type;
                    if ((0 == liv_type || 1 == liv_type || 2 == liv_type || 3 == liv_type)) { break; }
                }
                circ_stratc = cncData_lastNode_next->m_movepara.m_angleStart;
                circ_endc = cncData_lastNode_next->m_movepara.m_angleEnd;
                poslist[0] = fX;
                poslist[1] = fY;
                poslist[2] = circ_stratc;
                do
                {
                    m_liv_ZmcMoveRe = ZAux_Direct_MovePara(g_handle, 0, "MERGE", 0, 0);
                    if (m_liv_ZmcMoveRe)
                    {
                        Sleep(10);
                        if (h_ThreadMovePorc)return -1;
                    }
                } while (m_liv_ZmcMoveRe);
            do
                {
                    m_liv_ZmcMoveRe = ZAux_Direct_MoveAbsSp(g_handle, 3, axislist, poslist);
                    if (m_liv_ZmcMoveRe)
                    {
                        Sleep(10);
                        if (h_ThreadMovePorc)return -1;
                    }
                } while (m_liv_ZmcMoveRe);
                break;
            }
            case CNC_TRACK_LINE://1直线//直线不用转刀,但是如果G01与G01,不是G00与G01,运动结束后需要转刀
            {
                m_ShowData.Format(_T("G01 X%f Y%f  \r\n"), fX, fY);
                TRACE(m_ShowData);
                do {
                    m_liv_ZmcMoveRe = ZAux_Direct_MovePara(g_handle, 0, "MERGE", 0, 1);
                    if (m_liv_ZmcMoveRe)
                    {
                        Sleep(10);
                        if (h_ThreadMovePorc)return -1;
                    }
                } while (m_liv_ZmcMoveRe);
                if (ptr->m_pprev->m_movepara.m_type == CNC_TRACK_LINE || ptr->m_pprev->m_movepara.m_type == CNC_TRACK_ARC || ptr->m_pprev->m_movepara.m_type == CNC_TRACK_ARC_CCW)
                {
                    poslist[0] = fX;
                    poslist[1] = fY;
                    poslist[2] = ptr->m_movepara.m_angleEnd;
                    do
                    {
                        m_liv_ZmcMoveRe = ZAux_Direct_MoveAbs(g_handle, 3, axislist, poslist);
                        if (m_liv_ZmcMoveRe)
                        {
                            Sleep(10);
                            if (h_ThreadMovePorc)return -1;
                        }
                    } while (m_liv_ZmcMoveRe);
                    TRACE("\r\n");
                }
                else
                {
                    fX = ptr->m_movepara.m_end.x;
                    fY = ptr->m_movepara.m_end.y;
                    poslist[0] = fX;
                    poslist[1] = fY;
                    do
                    {
                        m_liv_ZmcMoveRe = ZAux_Direct_MoveAbs(g_handle, 2, axislist, poslist);
                        if (m_liv_ZmcMoveRe)
                        {
                            Sleep(10);
                            if (h_ThreadMovePorc)return -1;
                        }
                    } while (m_liv_ZmcMoveRe);
                }
                break;
            }
        }
        ptr = ptr->m_pnext;
    }
    return 0;
}

七.效果演示

效果演示1.png

效果演示2.png

效果演示3.png

效果演示4.png

视频讲解可点击→“PCIe EtherCAT实时运动控制卡PCIE464的CAD导图与刀向跟随应用”查看。


 

完整代码获取地址

二维码.png

本次,正运动技术PCIe EtherCAT实时运动控制卡PCIE464的CAD导图与刀向跟随应用,就分享到这里。

更多精彩内容请关注“正运动小助手”公众号,需要相关开发环境与例程代码,请咨询正运动技术销售工程师:400-089-8936。

本文由正运动技术原创,欢迎大家转载,共同学习,一起提高中国智能制造水平。文章版权归正运动技术所有,如有转载请注明文章来源。

 

二维码.png

正运动技术专注于运动控制技术研究和通用运动控制软硬件产品的研发,是国家级高新技术企业。正运动技术汇集了来自华为、中兴等公司的优秀人才,在坚持自主创新的同时,积极联合各大高校协同运动控制基础技术的研究,是国内工控领域发展最快的企业之一,也是国内少有、完整掌握运动控制核心技术和实时工控软件平台技术的企业。主要业务有:运动控制卡_运动控制器_EtherCAT运动控制卡_EtherCAT控制器_运动控制系统_视觉控制器__运动控制PLC_运动控制_机器人控制器_视觉定位_XPCIe/XPCI系列运动控制卡等。

版权所有 中华工控网 Copyright©2025 Gkong.com, All Rights Reserved