http://www.gkong.com 2025-05-08 14:58 深圳市正运动技术有限公司
PCIE464运动控制卡是正运动推出的一款EtherCAT总线+脉冲型、PCIE接口式的运动控制卡,可选6-64轴运动控制,支持多路高速数字输入输出,可轻松实现多轴同步控制和高速数据传输。
PCIE464运动控制卡适合于多轴点位运动、插补运动、轨迹规划、手轮控制、编码器位置检测、IO控制、位置锁存等功能的应用。
PCIE464运动控制卡适用于3C电子加工、检测设备、半导体设备、SMT加工、激光加工、光通讯设备、锂电及光伏设备、以及非标自动化设备等高速高精应用场合。
PCIE4系列控制卡的应用程序可以使用VC,VB,VS,C++,C#等软件开发,程序运行时需要动态库zmotion.dll,调试时可以将RTSys软件同时连接控制器,从而方便调试、方便观察。
PCIE464产品视频介绍可点击→“【EtherCAT同步周期快至100us】超高实时性PCIe EtherCAT控制卡PCIE464”查看。
更多关于PCIE464的详情介绍,点击“PCIE464 — 高速高精,超高实时性的PCIe EtherCAT实时运动控制卡”查看。
1.IN数字量输入接口
数字输入分布在J400(IN0-IN7)和X400(IN8-IN39)信号接口中。
2.OUT数字量输出接口
数字输出分布在J400(OUT0-7)和X400(OUT8-OUT39)信号接口中。
3.单端编码器及单端脉冲接线
单端脉冲接线图
差分脉冲接线图
单端编码器接线图
差分编码器接线图
注:PCIE464的J400接口中有一个差分脉冲轴接口和三个单端脉冲轴接口,两个差分编码器接口(其中一个与差分脉冲轴接口复用,取决于固件设定)和两个单端编码器接口,具体引脚定义参见PCIE464硬件手册。
1.CAD图纸解析
正运动技术提供开放的ZmotionCadEx库,可导入DXF、Ai、Plt、Dst图纸,可以生成运动坐标数据转G代码、zbasic运动指令、或直接PC函数执行运动。
2.刀向跟随计算
刀向跟随,是在插补运动的过程中,使非插补轴随着插补运动的合成位移的变化而变化,从而实现在加工过程中,刀具始终处于合适的加工方向和位置的工艺。
非插补轴(如旋转轴或独立轴)能够根据插补轴的合成位移实时调整,使刀具始终沿预定路径或方向运动。例如,在砂轮磨削中,通过调整砂轮的角度,确保其与工件切向方向一致;在布料裁切中,通过调整刀具朝向,始终指向行进方向。
本例中通过计算小线段的向量方向,以3轴插补形式运行实现,基本原理如下图所示。
已知线段起点A坐标,终点B坐标,在起点A上做与X轴平行向量AC,通过数学计算得到向量AB与向量AC的夹角α,即线段AB与X轴正方向夹角。将夹角α与终点坐标XY做三轴插补来作为刀向跟随。
在某些场合需要A轴先抬刀转到加工角度,再下刀加工:比如加工矩形轮廓四条边,那么应该先抬刀转到加工方向,再下刀进行加工,不应在工件上转刀,此时可以通过判断线段的长度,小于设定值时三轴插补,大于设定值时应抬刀→转刀→下刀再加工。
本例中主要用到了下面几个函数接口:
1.首先打开Visual Studio 2022,点击创建新项目。
2.选择开发语言为“Visual C++”和程序类型“MFC应用程序”。
3.点击下一步即可。
4.选择类型为“基于对话框”,下一步或者完成。
5.前往正运动官网下载PC函数库,路径如下(本文采用64位函数库为例)。
(1)进入官网,选择支持与服务,打开下载中心选择库文件,就能找到所有的PC函数库。
(2)点击下载Windows C++(64位),可按需求另存为想要保存的路径下。
(3)函数库另存为具体路径如下。
(4)如需处理CAD图,可与技术工程师联系获取ZmotionCadEx库。
6.将厂商提供的C++库文件和相关头文件复制到新建的项目里。
7.在项目中添加静态库和相关头文件。
(1)打开解决方案资源管理器,点击显示全部文件。
(2)选中.h与.lib文件,右键包括在项目中。
8.声明用到的头文件。
9.至此项目新建完成,可进行MFC项目开发。
1.PC函数手册也可以在正运动官网“支持与服务”→“下载中心”→“编程手册”中找到。
2.链接控制器,获取链接句柄。
3.查看ZmotionCadEx.h中关于CAD图处理的函数接口。
1.例程界面如下。
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; }
视频讲解可点击→“PCIe EtherCAT实时运动控制卡PCIE464的CAD导图与刀向跟随应用”查看。
完整代码获取地址
▼
本次,正运动技术PCIe EtherCAT实时运动控制卡PCIE464的CAD导图与刀向跟随应用,就分享到这里。
更多精彩内容请关注“正运动小助手”公众号,需要相关开发环境与例程代码,请咨询正运动技术销售工程师:400-089-8936。
本文由正运动技术原创,欢迎大家转载,共同学习,一起提高中国智能制造水平。文章版权归正运动技术所有,如有转载请注明文章来源。