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

C#运动控制开源(一): CAD导图和小线段速度前瞻的优化之CAD导图

http://www.gkong.com 2026-04-02 15:05 深圳市正运动技术有限公司

01

ZMC432-V2运动控制器介绍

ZMC432-V2高性能多轴运动控制器是一款兼容EtherCAT总线和脉冲型的独立式运动控制器,自带6轴本地差分脉冲轴,最多可扩展至32轴,能实现总线轴+脉冲轴混合插补的多轴运动控制场合。同时支持正运动远程显示功能,能提供网络组态显示,可实时监控和调整参数配置。

正运动ZMC432-V2运动控制器介绍.webp

ZMC432-V2硬件功能特性:

(1)支持32轴运动控制(脉冲+EtherCAT总线),EtherCAT最小通讯周期可达125us;

(2)24路通用输入、12路通用输出,2路模拟量输出(DA),其中包括2路高速输入和2路高速输出;

(3)6路差分脉冲轴输出,总线轴、脉冲轴可混合插补;

(4)内置多项实时性运动控制功能,例如视觉飞拍、多维PSO、高速位置锁存,多轴同步运行等;

(5)可通过EtherCAT扩展模块进行IO硬件资源扩展,可扩展至4096个隔离输入口和4096个隔离输出口;

(6)具备丰富的运动控制功能,如点位运动、电子凸轮、直线插补、圆弧插补、连续轨迹加工;

(7)支持掉电检测、掉电存储,多种程序加密方式,能够有效防止系统故障,保护项目工程文件数据,并提高系统的可靠性;

(8)通过纯国产IDE开发环境RTSys进行项目开发,可实时仿真、在线跟踪以及诊断与调试,简便易用,支持多种高级上位机语言联合编程进行二次开发。

正运动ZMC432-V2运动控制器架构图.webp

02

C#运动控制+CAD导图DEMO概述

本期示教DEMO是以正运动的运动控制函数库,CAD导图函数库,在VS环境下使用C#进行编程开发。

DEMO内容主要实现CAD图纸解析(导入CAD文件,轨迹数据解析,编辑轨迹)后下发给控制器进行运动(运动前瞻,运动指令下发,状态监控)。用户可以参考例程更快的使用正运动函数库进行相关开发。

后期我们将推出以下3篇教程介绍该示教例程的开发流程和使用方法,方便用户快速上手该例程,并掌握C#运动控制与CAD导图相结合编程开发的相关知识。

? C#运动控制开源(一): CAD导图和小线段速度前瞻的优化之CAD导图

? C#运动控制开源(二): CAD导图和小线段速度前瞻的优化之前瞻优化

? C#运动控制开源(三): CAD导图和小线段速度前瞻的优化的软件框架

03

C#使用ZMOTION CAD库进行CAD导图的开发

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

1.在VS2019菜单“文件”→“新建”→“项目”,启动创建项目向导。

3.1.webp

2.选择开发语言为“C#”和Windows窗体应用程序,点击下一步。

3.2.webp

3.配置好项目名称和位置,以及相应框架,点击创建。

3.3.webp

4.找到厂家提供的光盘资料里面的C#函数库,路径如下(64位库为例)。

进入厂商提供的光盘资料,找到ZmotionCadEx.dll,ZmotionCadEx.cs这两个个库文件。库文件路径:【00光盘资料】→【04PC函数】→【03Zmotion CAD库V3.1】→【库文件】→【Windows平台】→【C#】→【64位】。

3.4.webp

5.将厂商提供的C#的库文件以及相关文件复制到新建的项目中。

(1)将ZmotionCadEx.cs文件复制到新建的项目里面中。

3.5.1.webp

(2)将ZmotionCadEx.dll文件放入bin\debug文件夹中。

3.5.2.webp

(3)将ZmotionCadEx.cs文件添加进项目中。右键项目名称,选择添加,再选择现有项,选择ZmotionCadEx.cs文件。

3.5.3.webp

6.双击Form1.cs里面的Form1,出现代码编辑界面,在文件开头写入using ZmotionCadDll。

3.6.webp

至此,项目新建完成,可进行C#项目开发。

例程界面如下:

例程界面.webp

CAD解析与编辑流程:

CAD解析与编辑流程.webp

04

实现CAD文件解析与显示

1.CAD解析相关函数介绍

①连接控制器。

Description:    //与控制器建立链接,成功后解锁高级功能
Input:          //IP地址,字符串的方式输入
Output:         //控制器句柄
Return:         //错误码
int32 __stdcall ZMotionCadArray_OpenEth(char *ipaddr, ZMC_HANDLE *pHandle);
*************************************************************/
[DllImport("ZmotionCadEx.dll", EntryPoint = "ZMotionCadArray_OpenEth", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern Int32 ZMotionCadArray_OpenEth(string ipaddr, out IntPtr phandle);

②导入CAD图形。

/*************************************************************
Description:    //导入图形文件(支持dxf、plt、ai、dst)
Input:          //
    lpszFileFullPathname 路径和文件名
    duUnit  PLT的比例    Option   预留, 缺省都转换为seg
    refDistance    转换时参考精度
Output:         //
Return:         //错误码
int __stdcall ZMotionCadArray_ImportVectGraph(LPCTSTR lpszFileFullPathname, double duUnit, int Option, double refDistance);
*************************************************************/
[DllImport("ZmotionCadEx.dll", EntryPoint = "ZMotionCadArray_ImportVectGraph", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern Int32 ZMotionCadArray_ImportVectGraph(string lpszFileFullPathname, double duUnit, int Option, double refDistance);

③提取图形数组。

/*************************************************************
Description:    // 提取当前图形数组
Input:          //struct_Array  提取的数组
                //nStructNum    数组的数量
Output:         //
Return:         //错误码
*************************************************************/
//int __stdcall ZMotionCadArray_GetVectArray(Struct_ZCad_Array *struct_Array, int nStructNum);
[DllImport("ZmotionCadEx.dll", EntryPoint = "ZMotionCadArray_GetVectArray", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern Int32 ZMotionCadArray_GetVectArray(ref Struct_ZCad_Array struct_Array, int nStructNum);

④图形数组格式。

④图形数组格式.webp

⑤获取CAD图形的范围。

/*************************************************************
Description:    // 范围   坐标方向是向上为正
Input:          //
Output:         //
Return:         //错误码
*************************************************************/
//uint32  __stdcall ZMotionCadArray_GetRange(float *pLeft, float *pBottom, float *pWdith, float *pHeight, double refDistance);
[DllImport("ZmotionCadEx.dll", EntryPoint = "ZMotionCadArray_GetRange", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern Int32 ZMotionCadArray_GetRange(ref float pLeft, ref float pBottom, ref float pWdith, ref float pHeight, double refDistance);

2.解析流程

步骤1:连接控制器并导入需要解析的CAD文件。

ZmotionCad.ZMotionCadArray_ImportVectGraph导入CAD文件(支持DXF、Ai、Plt、Dst格式);

参数3可以设置是否把曲线强制转换为小线段,本例程是转化成小线段解析的。

使用ZMotionCadArray_GetRange获取到CAD图形的范围,并和显示用的PictureBox长宽进行计算,获得转换比例和偏移。

if(G_CardHandle == (IntPtr)0)
{
    MessageBox.Show("检测到尚未连接控制器,请先连接控制器再进行操作");
    return;
}
if (G_CadHandle == (IntPtr)0)
{
    iret = ZmotionCad.ZMotionCadArray_OpenEth(连接控制器.Adrr, out G_CadHandle);
}
if (G_CadHandle != (IntPtr)0)       //开始导入CAD文件
{
    OpenFileDialog openFileDialog1 = new OpenFileDialog();
    openFileDialog1.InitialDirectory = "\\";
    openFileDialog1.Filter = "DXF File(*.dxf)|*.dxf|PLT File(*.PLT)|*.PLT|AI File(*.AI)|*.AI|DST File(*.DST)|*.DST";
    openFileDialog1.RestoreDirectory = true;
    openFileDialog1.FilterIndex = 1;
    if (openFileDialog1.ShowDialog() == DialogResult.OK)            //打开配置文件
    {
        strFilePath = openFileDialog1.FileName;
        this.Text = strFilePath; 
         iret = ZmotionCad.ZMotionCadArray_ImportVectGraph(strFilePath, 1024,0, m_refDistance);     //曲线强制转换为小线段
        iret = ZmotionCad.ZMotionCadArray_GetVectNum(ref ZCad_ArrayLen);           //导入数据
        ZCad_ArrayInfo = new ZmotionCad.Struct_ZCad_Array[ZCad_ArrayLen];
        iret = ZmotionCad.ZMotionCadArray_GetVectArray(ref ZCad_ArrayInfo[0], ZCad_ArrayLen);         //获取图形数据
        iret = ZmotionCad.ZMotionCadArray_IfCloseVect(false);             //是否只处理封闭轨迹 
         Get_Array();
        float Image_Left, Image_bottom, Image_Width, Image_Height;
        Image_Left = 0;
        Image_bottom = 0;
        Image_Width = 0; 
         Image_Height = 0;
        iret = ZmotionCad.ZMotionCadArray_GetRange(ref Image_Left, ref Image_bottom, ref Image_Width, ref Image_Height, 0.05);
        if (Image_Width < 0.0001 && Image_Height < 0.0001)
        {
            Image_Left = (float)0.0;
            Image_bottom = (float)0.0;
            Image_Width = (float)100.0;
            Image_Height = (float)100.0;
        }
        double ObjectPixHeight, ObjectPixWidth;
        if (Image_Width * PicHeight <= Image_Height * PicWidth)
        {
            ObjectPixHeight = PicHeight;
            ObjectPixWidth = ObjectPixHeight * Image_Width / Image_Height;
        }
        else
        {
            ObjectPixWidth = PicWidth;
            ObjectPixHeight = ObjectPixWidth * Image_Height / Image_Width;
        }
        zoomFactor = 1;
        dScale = ObjectPixHeight / Image_Height;
        m_dUnitsPerMm = dScale * 1;
        //偏移
        m_dTranX = (CadShow.Width - ObjectPixWidth) / 2 - Image_Left * dScale;
        m_dTranY = (CadShow.Height - ObjectPixHeight) / 2 - Image_bottom * dScale;
        Show_Picture();
        If_ImportArray = true;
    }
}
else
{
    MessageBox.Show("控制器连接失败");
}

步骤2:通过函数获取VectArray数据并解析。

使用ZMotionCadArray_GetVectArray获取到图形数组,通过Show_Picture()解析图形数组并绘制到PictureBox图象上显示。

//获取数据
public void Get_Array()
{
    if (G_CadHandle == (IntPtr)0)
    {
        //MessageBox.Show("未链接到控制器!", "提示");
    }
    else
    {
        int iret = ZmotionCad.ZMotionCadArray_GetVectNum(ref ZCad_ArrayLen);           //获取图形长度
        ZCad_ArrayInfo = new ZmotionCad.Struct_ZCad_Array[ZCad_ArrayLen];
        iret = ZmotionCad.ZMotionCadArray_GetVectArray(ref ZCad_ArrayInfo[0], ZCad_ArrayLen);         //获取图形数据
          choosevectnum = 0;
        closevectnum = 0;
        for (int i = 0; i < ZCad_ArrayLen; i++)                //遍历数组
        {
            if ((ZCad_ArrayInfo[i].m_nInVectFrist == 1))     //是曲线起始
            {
                if (ZCad_ArrayInfo[i].m_nChoose == 1)                 //是否选中
                {
                    choosevectnum++;
                }
                closevectnum++;
            }
        }
        Show_Picture();
    }
}
解析图形数组和绘图函数Show_Picture()
//显示图形
public void Show_Picture()
{    g.Clear(Color.Black);    if (坐标系ToolStripMenuItem.Checked)
    {
        //绘制坐标系
        My_Pen = new Pen(Color.Red, 1); 
       g.DrawLine(My_Pen, (int)(0 * m_dUnitsPerMm + m_dTranX), CadShow.Height - (int)(0 * m_dUnitsPerMm + m_dTranY), (int)CadShow.Width, CadShow.Height - (int)(0 * m_dUnitsPerMm + m_dTranY));
        My_Pen = new Pen(Color.Green, 1);
        g.DrawLine(My_Pen, (int)(0 * m_dUnitsPerMm + m_dTranX), CadShow.Height - (int)(0 * m_dUnitsPerMm + m_dTranY), (int)(0 * m_dUnitsPerMm + m_dTranX), 0);        //原点
        My_Pen = new Pen(Color.White, 1);
        g.DrawRectangle(My_Pen, (int)(-10 * m_dUnitsPerMm + m_dTranX), CadShow.Height - (int)(10 * m_dUnitsPerMm + m_dTranY), (int)(20 * m_dUnitsPerMm), (int)(20 * m_dUnitsPerMm));
        //标注XY方向
        My_Pen = new Pen(Color.White, 1);
        g.DrawLine(My_Pen, (int)(0 * m_dUnitsPerMm + m_dTranX), CadShow.Height - (int)(0 * m_dUnitsPerMm + m_dTranY), (int)(0 * m_dUnitsPerMm + m_dTranX + 128), CadShow.Height - (int)(0 * m_dUnitsPerMm + m_dTranY));
        My_Pen = new Pen(Color.White, 1);
        g.DrawLine(My_Pen, (int)(0 * m_dUnitsPerMm + m_dTranX), CadShow.Height - (int)(0 * m_dUnitsPerMm + m_dTranY), (int)(0 * m_dUnitsPerMm + m_dTranX), CadShow.Height - (int)(0 * m_dUnitsPerMm + m_dTranY + 128));
        //标注XY字符
        My_Pen = new Pen(Color.White, 1);
        Draw_String((int)(0 * m_dUnitsPerMm + m_dTranX + 124), CadShow.Height - (int)(0 * m_dUnitsPerMm + m_dTranY + 11), "X");
        My_Pen = new Pen(Color.White, 1);
        Draw_String((int)(0 * m_dUnitsPerMm + m_dTranX - 9), CadShow.Height - (int)(0 * m_dUnitsPerMm + m_dTranY + 150), "Y");
    }
    for (int i = 0; i < ZCad_ArrayLen; i++)                //遍历数组
    {
        double dXPrevPos_x1, dXPrevPos_x2, dXPrevPos_z1, dXPrevPos_y1, dXPrevPos_y2, dXPrevPos_z2; 
       dXPrevPos_x1 = ZCad_ArrayInfo[i].x1 * m_dUnitsPerMm + m_dTranX;
        dXPrevPos_x2 = ZCad_ArrayInfo[i].x2 * m_dUnitsPerMm + m_dTranX;
        dXPrevPos_z1 = ZCad_ArrayInfo[i].z1 / 3.141596 * 180;
        dXPrevPos_y1 = CadShow.Height - (ZCad_ArrayInfo[i].y1 * m_dUnitsPerMm + m_dTranY);
        dXPrevPos_y2 = CadShow.Height - (ZCad_ArrayInfo[i].y2 * m_dUnitsPerMm + m_dTranY);
        dXPrevPos_z2 = ZCad_ArrayInfo[i].z2 / 3.141596 * 180;
        double Start_Pos_x, Start_Pos_y;
        switch (ZCad_ArrayInfo[i].m_nItemtype)
        {
            case ZmotionCad.ZCAD_ITEMTYPE_VECT:        //此处开始是曲线类型
                 break;
            case ZmotionCad.ZCAD_ITEMTYPE_VECTPoint:   //点
                if (ZCad_ArrayInfo[i].m_nEmptyMove != 0)
                {
                }
                break;
            case ZmotionCad.ZCAD_ITEMTYPE_VECTLine:    //通过线型绘制图形
                if (ZCad_ArrayInfo[i].m_nInVectFrist == 1)                 //是否曲线起始
                {
                    if (ZCad_ArrayInfo[i].m_nChoose == 1)                 //是否选中
                        iCloseChoose += 1;
                    Start_Pos_x = ZCad_ArrayInfo[i].m_dGetStartX * m_dUnitsPerMm + m_dTranX;
                    Start_Pos_y = CadShow.Height - (ZCad_ArrayInfo[i].m_dGetStartY * m_dUnitsPerMm + m_dTranY);
                    if (顺序ToolStripMenuItem.Checked)
                    {
                        Draw_String((int)Start_Pos_x, (int)Start_Pos_y, iCloseLine.ToString());          //标记数字
                    }
                    if (空移ToolStripMenuItem.Checked)
                    {
                        p_color = Color.Gray;                        //默认颜色
                        My_Pen = new Pen(p_color, (float)0.1);              //画笔颜色,宽度
                        My_Pen.DashPattern = new float[] { 3, 3 };                                     //设置短划线和空白部分的数组
                        Draw_Line((int)PrePosx, (int)PrePosy, (int)dXPrevPos_x1, (int)dXPrevPos_y1);
                    } 
                   iCloseLine++;
                }
                if (ZCad_ArrayInfo[i].m_nChoose == 1)                 //是否选中
                {
                    p_color = Color.Red;                        //默认颜色
                    My_Pen = new Pen(p_color, 1);              //画笔颜色,宽度
                }
                else
                {
                    p_color = Color.White;                        //默认颜色
                    My_Pen = new Pen(p_color, 1);              //画笔颜色,宽度
                     }                // 计算线段长度
                float dx = (int)dXPrevPos_x1 - (int)dXPrevPos_x2; 
               float dy = (int)dXPrevPos_y1 - (int)dXPrevPos_y2;
                float dis = (float)Math.Sqrt(dx * dx + dy * dy);
                TotalDis += dis;
                if (起点ToolStripMenuItem.Checked)
                {
                    if (ZCad_ArrayInfo[i].m_nChoose == 1)
                        g.FillEllipse(Brushes.Red, (float)(dXPrevPos_x1 - 2), (float)(dXPrevPos_y1 - 2), 4, 4);
                    else
                        g.FillEllipse(Brushes.White, (float)(dXPrevPos_x1 - 2), (float)(dXPrevPos_y1 - 2), 4, 4);
                }
                if ((TotalDis > 50) && (方向ToolStripMenuItem.Checked))
                {
                    Draw_Arrow((int)dXPrevPos_x1, (int)dXPrevPos_y1, (int)dXPrevPos_x2, (int)dXPrevPos_y2,5, true);
                    TotalDis = 0;
                }
                else
                {
                    Draw_Line((int)dXPrevPos_x1, (int)dXPrevPos_y1, (int)dXPrevPos_x2, (int)dXPrevPos_y2);
                }
                PrePosx = dXPrevPos_x2;
                PrePosy = dXPrevPos_y2;
                break;
            //    break;
            default:
                break;
        }
    }
}

05

实现CAD文件编辑修改

1.CAD编辑相关函数介绍。

/*************************************************************
Description:    // 新建一个对象并插入到图层的末尾
Input:          //struct_Vect  新建的对象
Output:         //
Return:         //错误码
int __stdcall ZMotionCadArray_NewOne(Struct_ZCad_Array struct_NewVect);
*************************************************************/
[DllImport("ZmotionCadEx.dll", EntryPoint = "ZMotionCadArray_NewOne", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern Int32 ZMotionCadArray_NewOne(Struct_ZCad_Array struct_NewVect);
/*************************************************************
Description:    // 删除指定对象
Input:          //nDelVect  需要删除的对象的序号
Output:         //
Return:         //错误码
int __stdcall ZMotionCadArray_DelOne(int nDelVect);
*************************************************************/
[DllImport("ZmotionCadEx.dll", EntryPoint = "ZMotionCadArray_DelOne", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern Int32 ZMotionCadArray_DelOne(int nDelVect);
/*************************************************************
Description:    // 移动对象
Input:          // m_x         x方向移动的距离
                // m_y         y方向移动的距离
                // nMoveVect   需要移动的对象的序号,-1为移动所有
Output:         //Return:         //错误码
*************************************************************/
//uint32  __stdcall ZMotionCadArray_Move(double m_x, double m_y, int nMoveVect);
[DllImport("ZmotionCadEx.dll", EntryPoint = "ZMotionCadArray_Move", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern Int32 ZMotionCadArray_Move(double m_x, double m_y, int nMoveVect);
/*************************************************************
Description:    // 缩放对象
Input:          //nScaleVect   需要缩放的对象的序号,-1为缩放所有
Output:         //Return:         //错误码
*************************************************************/
//uint32  __stdcall ZMotionCadArray_Scale(float scaleX, float scaleY, float pointx, float pointy, int nScaleVect);
[DllImport("ZmotionCadEx.dll", EntryPoint = "ZMotionCadArray_Scale", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern Int32 ZMotionCadArray_Scale(float scaleX, float scaleY, float pointx, float pointy, int nScaleVect);
/*************************************************************
Description:    // 插入对象
Input:          // nArrayNum    输入的数组数量
                // nInsertNo    插入的位置
Output:         //
Return:         //错误码
*************************************************************/
//uint32  __stdcall ZMotionCadArray_ItemInsert(Struct_ZCad_Array *struct_NewVect, int nArrayNum, int nInsertNo);
[DllImport("ZmotionCadEx.dll", EntryPoint = "ZMotionCadArray_ItemInsert", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern Int32 ZMotionCadArray_ItemInsert(ref Struct_ZCad_Array struct_NewVect, int nArrayNum, int nInsertNo);/*************************************************************
Description:    // 修改对象
Input:          // nArrayNum    输入的数组数量
                // nInsertNo    修改的对象位置
Output:         //
Return:         //错误码
*************************************************************/
//uint32  __stdcall ZMotionCadArray_ItemModify(Struct_ZCad_Array *struct_NewVect, int nArrayNum, int nModifyNo);
[DllImport("ZmotionCadEx.dll", EntryPoint = "ZMotionCadArray_ItemModify", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern Int32 ZMotionCadArray_ItemModify(ref Struct_ZCad_Array struct_NewVect, int nArrayNum, int nModifyNo);

2.编辑流程。

步骤1:通过PictureBox鼠标事件响应函数实现框选功能。

鼠标按下响应函数
private void MyPicture_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        Point_MouseDown = new Point(e.X, e.Y);
        if (newdrawtype == 0)  //框选
            Start_Choose = true;
    }
}
鼠标移动响应函数
private void MyPicture_MouseMove(object sender, MouseEventArgs e)
{
    Point_MouseCur = new Point(e.X, e.Y);
    //Show_Picture();
    if (e.Button == MouseButtons.Left)
    {
        CadShow.Invalidate();
    }
}
鼠标松开响应函数
private void MyPicture_MouseUp(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        double dPrevXPos, dPrevYPos, dCurXPos, dCurYPos;
        dPrevXPos = (double)((Point_MouseDown.X - m_dTranX) / m_dUnitsPerMm);
        dPrevYPos = (double)((CadShow.Height - Point_MouseDown.Y - m_dTranY) / m_dUnitsPerMm);
        dCurXPos = (double)((Point_MouseCur.X - m_dTranX) / m_dUnitsPerMm);
        dCurYPos = (double)((CadShow.Height - Point_MouseCur.Y - m_dTranY) / m_dUnitsPerMm);
        if (newdrawtype == 0)       //框选
        {
            if (Start_Choose == false)             //更改起点完成
            {
                return;
            }
            Start_Choose = false;
            if (G_CadHandle == (IntPtr)0)
            {
                //MessageBox.Show("未链接到控制器!", "提示");
            }
            else
            {
                int iret = 0;
                if (Point_MouseDown.X > Point_MouseCur.X)                             //左选
                {
                    iret = ZmotionCad.ZMotionCadArray_SelRightToLeft(dPrevXPos, dPrevYPos, dCurXPos, dCurYPos, false);
                }
                else if (Point_MouseDown.X < Point_MouseCur.X)                         //右选
                {
                    iret = ZmotionCad.ZMotionCadArray_SelLeftToRight(dPrevXPos, dPrevYPos, dCurXPos, dCurYPos, false);
                }
                else
                {
                    iret = ZmotionCad.ZMotionCadArray_SelOne(dPrevXPos, dPrevYPos, 5.0 / m_dUnitsPerMm, false);
                }
            }
        }
    }
}

步骤2:对选中图案进行平移操作。

通过按钮对选中图案进行平移,对应平移函数movechoose,xy为平移相对距离
ZMotionCadArray_Move参数3传的值是遍历m_nInVectFrist,=1的时候表示vect曲线第一段,传的是vect曲线的编号
public void movechoose(double x,double y)
{
    int iclosenum = 0;
    int iclosechoose = 0;
    uint iret = 0;
    for (int i = 0; i < ZCad_ArrayLen; i++)                //遍历数组
    {
        if ((ZCad_ArrayInfo[i].m_nInVectFrist == 1))     //是曲线起始
        {
            if (ZCad_ArrayInfo[i].m_nChoose == 1)                 //是否选中
            {
                if (ZCad_ArrayInfo[i].m_nItemtype == ZCAD_ITEMTYPE_VECTLine)
                {
                    ZmotionCad.ZMotionCadArray_Move(x, y, iclosenum);
                    Get_Array();
                }
                iclosechoose++;
            }
            iclosenum++;
        }
    }
}

步骤3:删除选中图案。

ZMotionCadArray_DelOne和ZMotionCadArray_Move一样需要传的是vect曲线的编号
private void CadDel_Click(object sender, EventArgs e)
{
    while (choosevectnum>0)
    {
        int iret = ZmotionCad.ZMotionCadArray_GetVectNum(ref ZCad_ArrayLen);           //获取图形长度
        ZCad_ArrayInfo = new ZmotionCad.Struct_ZCad_Array[ZCad_ArrayLen];
        iret = ZmotionCad.ZMotionCadArray_GetVectArray(ref ZCad_ArrayInfo[0], ZCad_ArrayLen);         //获取图形数据
          choosevectnum = 0;
        closevectnum = 0;
        for (int i = 0; i < ZCad_ArrayLen; i++)                //遍历数组
        {
            if ((ZCad_ArrayInfo[i].m_nInVectFrist == 1))     //是曲线起始
            {
                if (ZCad_ArrayInfo[i].m_nChoose == 1)                 //是否选中 
               {
                    choosevectnum++;
                }
                closevectnum++;
            }
        }
        int iclosenum = 0;
        for (int i = 0; i < ZCad_ArrayLen; i++)                //遍历数组
        {
            if ((ZCad_ArrayInfo[i].m_nInVectFrist == 1))     //是曲线起始
            {
                if (ZCad_ArrayInfo[i].m_nChoose == 1)                 //是否选中
                {
                    ZmotionCad.ZMotionCadArray_DelOne(iclosenum); 
                   break;
                }
                iclosenum++;
            }
        }
    }
    Get_Array();
}

步骤4:插入新图案。

通过界面按钮选择插入新图案类型,利用PicBox鼠标响应事件获取插入图案所在点位,使用ZMotionCadArray_NewOne或者ZMotionCadArray_ItemInsert
插入VectArray图案数组插入圆弧或者整圆时可以通过ZMotionOptimize_TransArcSeges分解成小线段再插入VectArray
private void MyPicture_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        Point_MouseDown = new Point(e.X, e.Y);
        if (newdrawtype == 0)  //框选
            Start_Choose = true;
        else if (newdrawtype == 2)  //多线段
        {
            multpointX[multnum] = Point_MouseDown.X;
            multpointY[multnum] = Point_MouseDown.Y;
            multnum++;
        }
        else if(newdrawtype == 4)//三点圆弧
        {
            multpointX[multnum] = Point_MouseDown.X;
            multpointY[multnum] = Point_MouseDown.Y;
            multnum++;
            if (multnum == 3)       //三点圆弧
            {
                double startrad, endrad;
                startrad = Math.Atan2(multpointY[1] - multpointY[0], multpointX[1] - multpointX[0]);
                endrad = Math.Atan2(multpointY[2] - multpointY[1], multpointX[2] - multpointX[1]) ;
                double dPrevXPos, dPrevYPos, dCurXPos, dCurYPos;
                dPrevXPos = (double)((multpointX[0] - m_dTranX) / m_dUnitsPerMm);
                dPrevYPos = (double)((CadShow.Height - Point_MouseDown.Y - m_dTranY) / m_dUnitsPerMm);
                dCurXPos = (double)((Point_MouseCur.X - m_dTranX) / m_dUnitsPerMm);
                dCurYPos = (double)((CadShow.Height - Point_MouseCur.Y - m_dTranY) / m_dUnitsPerMm);
                //圆弧拆分小线段处理
                int ilen = -1;
                double[] ArcToLineX = new double[1000];
                double[] ArcToLineY = new double[1000];
                //获取转换长度
                int iret = UserCad.ZMotionOptimize_TransArcSeges(G_CardHandle, (double)((multpointX[0] - m_dTranX) / m_dUnitsPerMm), (double)((CadShow.Height - multpointY[0] - m_dTranY) / m_dUnitsPerMm), (double)((multpointX[1] - m_dTranX) / m_dUnitsPerMm), (double)((CadShow.Height - multpointY[0] - m_dTranY) / m_dUnitsPerMm), startrad, endrad-startrad, m_refDistance, ArcToLineX, ArcToLineY, ref ilen);
                ArcToLineX = new double[ilen];
                ArcToLineY = new double[ilen];
                //获取数据
                iret = UserCad.ZMotionOptimize_TransArcSeges(G_CardHandle, (double)((multpointX[0] - m_dTranX) / m_dUnitsPerMm), (double)((CadShow.Height - multpointY[0] - m_dTranY) / m_dUnitsPerMm), (double)((multpointX[1] - m_dTranX) / m_dUnitsPerMm), (double)((CadShow.Height - multpointY[0] - m_dTranY) / m_dUnitsPerMm), startrad, endrad - startrad, m_refDistance, ArcToLineX, ArcToLineY, ref ilen);
                ZmotionCad.Struct_ZCad_Array[] ZcadNew = new ZmotionCad.Struct_ZCad_Array[ilen];   //拆分出来的小线段
                ZcadNew[0] = ZCad_ArrayInfo[ZCad_ArrayLen - 1];
                ZcadNew[0].m_nItemtype = ZCAD_ITEMTYPE_VECTLine;
                ZcadNew[0].m_nInVectFrist = 1;
                ZcadNew[0].m_nEmptyMove = 1;
                ZcadNew[0].m_dGetStartX = ArcToLineX[0];
                ZcadNew[0].m_dGetStartY = ArcToLineY[0];
                ZcadNew[0].x1 = ZcadNew[0].m_dGetStartX;
                ZcadNew[0].y1 = ZcadNew[0].m_dGetStartY;
                ZcadNew[0].x2 = ZcadNew[0].m_dGetStartX;
                ZcadNew[0].y2 = ZcadNew[0].m_dGetStartY;
                ZmotionCad.ZMotionCadArray_NewOne(ZcadNew[0]);
                for (int i = 1; i < ilen; i++)
                {
                    ZcadNew[i] = ZcadNew[0];
                    ZcadNew[i].m_nInVectFrist = 0;
                    ZcadNew[i].m_nEmptyMove = 0;
                    ZcadNew[i].m_dGetStartX = ArcToLineX[0];
                    ZcadNew[i].m_dGetStartY = ArcToLineY[0];
                    ZcadNew[i].x1 = ArcToLineX[i - 1];
                    ZcadNew[i].y1 = ArcToLineY[i - 1];
                    ZcadNew[i].x2 = ArcToLineX[i];
                    ZcadNew[i].y2 = ArcToLineY[i];
                    ZmotionCad.ZMotionCadArray_NewOne(ZcadNew[i]);
                }
                newdrawtype = 0;
                multnum = 0;
            }
        }
    }
}

06

DEMO效果演示

1.点击控制器→连接控制器。

6.1.webp

2.点击文件→打开,选择对应CAD文件。

6.2.webp

3.打开后显示图形,此时可以方向键进行平移或者鼠标滚轮进行缩放操作。

6.3.webp

4.点击编辑可以进行图形优化或者排序操作。

6.4.webp

5.视图中可以选择空移,顺序,标号坐标系的显示。

6.5.webp

6.点击右侧编辑标签,进入编辑界面,此时可以框选选中需要编辑的图案。

6.6.webp

7.设置好移动距离,并点击上下左右移动,可以平移选中图案。

6.7.webp

8.点击删除可以删除对应图案。

6.8.webp

9.点击添加图形中的图案类型,可以添加新图案。

6.9.webp

教学视频请点击→C#运动控制开源(一): CAD导图和小线段速度前瞻的优化之CAD导图

完整代码获取地址

例程二维码.webp

本次,正运动技术C#运动控制开源(一):CAD导图和小线段速度前瞻的优化之CAD导图,就分享到这里。

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

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

二维码.webp

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

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