正运动 2025-5-22 08:07:22 | 显示全部楼层 | 阅读模式
抖音粉丝群1
『7x24小时有问必答』

AI浪潮下的计算机行业——从业者现状与未来展望
上节课程给大家介绍了ZMC432CL-V2硬件接口(详情点击→步进的光栅尺全闭环EtherCAT运动控制器ZMC432CL-V2(一):硬件接口介绍)。

本节主要讲解如何通过C#编写程序调试ZMC432CL-V2的脉冲闭环功能。


一、ZMC432CL-V2产品简介

ZMC432CL-V2高性能多轴运动控制器是一款兼容EtherCAT总线和脉冲型的独立式运动控制器,具备高速实时反馈功能,支持脉冲全闭环控制,能够实现高精度、高响应速度的运动控制。高精度定位,有效消除机械传动误差,满足高精密加工场景应用要求。

1.ZMC432CL-V2硬件功能

(1)丰富的运动控制功能:支持直线、圆弧、空间圆弧、螺旋插补等。
(2)硬件接口丰富:支持脉冲轴(带编码器反馈)和EtherCAT总线轴,具备24路输入和12路输出的通用IO,部分为高速IO,2路模拟量输出(DA)。
(3)EtherCAT刷新周期最快达250us,满足高速通信需求。
(4)支持4通道硬件比较输出、硬件定时器、运动中精准输出,适用于多通道视觉飞拍等场合。
(5)支持掉电检测、掉电存储,多种程序加密方式,能够有效防止系统故障,保护项目工程文件数据,并提高系统的可靠性。
(6)通过纯国产IDE开发环境RTSys进行项目开发,可实时仿真、在线跟踪以及诊断与调试,简便易用,支持多种高级上位机语言联合编程进行二次开发。

ZMC432CL-V2产品介绍视频

更多关于ZMC432CL-V2详情介绍点击→步进控制的光栅尺全闭环解决方案:32轴EtherCAT总线运动控制器ZMC432CL-V2。


二、C#语言如何调用ZMotion的动态库进行项目开发

(一)新建WinForm项目并添加函数库

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

2.选择开发语言为“Visual C#”和.NET Framework 4以及Windows窗体应用程序。

3.找到厂家提供的光盘资料里面的C#函数库,路径如下。

1)进入厂商提供的光盘资料找到“04PC函数”文件夹,并点击进入。

2)选择“01 PC函数库V2.1”文件夹。

3)选择“Windows平台”文件夹。

4)选择“C#文件夹,里面有32位和64位的动态库和例程。

4.将厂商提供的C#的库文件以及相关文件复制到新建的项目中。
1)将Zmcaux.cs文件复制到新建的项目里面中。

2)将zaux.dll和zmotion.dll文件放入bin\debug文件夹中。

5.用vs打开新建的项目文件,在右边的解决方案资源管理器中点击显示所有文件,然后鼠标右击Zmcaux.cs文件,点击包括在项目中。

6.双击Form1.cs里面的Form1,出现代码编辑界面,在文件开头写入using cszmcaux,并声明控制器句柄g_handle。


三、PC函数介绍

1.PC函数手册可在光盘资料查看,具体路径如下。

2.控制器/卡接口之链接控制器,获取链接句柄。

指令11

ZAux_FastOpen

指令原型

int32  __stdcall  ZAux_FastOpen(int type, char *pconnectstring, uint32 uims ,ZMC_HANDLE * phandle)

指令说明

与控制器建立连接, 可以指定连接的等待时间

输入参数

参数名

描述

type

连接类型

type: 1-COM 2-ETH 4-PCI 5-LOCAL

pconnectstring

连接字符串:

type=1

COM+串口号type=2:IP 地址

type=4:Pci+卡号

type=5:LOCAL+卡号



uims

连接超时时间 uims;单位ms

输出参数

参数名

描述

phandle

控制器连接句柄

返回值

成功返回值为0,非0详见错误码说明。

指令示例

串口连接:

ZMC_HANDLE  phandle;//控制器连接句柄

Char comID[32]= "0";//串口ID

ZAux_FastOpen(1, comID,1000 ,&phandle);

网口连接例子:

ZMC_HANDLE  phandle;//控制器连接句柄

Char EthID[32]= "192.168.0.11";//网口ID

ZAux_FastOpen(1, EthID,1000 ,&phandle);

详细说明

/

3.万能指令之在线命令。

有一些使用频率较低的Basic指令我们没有封装到上位机的辅助库中,如果用户上位机需要调用对应的Basic指令的话,可以通过在线命令自行进行相关指令封装。

指令265

ZAux_DirectCommand

指令原型

int32  __stdcall  ZAux_DirectCommand(ZMC_HANDLE handle, const char  *pszCommand,char  *psResponse, uint32 uiResponseLength)

指令说明

发送字符串命令到控制器,直接方式(不进缓冲区,有少数Basic指令暂时不支持推荐使用)。

输入参数

参数名

描述

handle

连接句柄。

pszCommand

发送的命令字符串。

uiResponseLength

返回的字符长度。

输出参数

参数名

描述

uiResponseLength

返回的字符串。

返回值

成功返回值为0,非0详见错误码说明。

指令示例

在线命令函数的使用

详细说明

上位机调用上位机未封装的Basic指令功能

指令264
ZAux_Execute

指令原型

int32  __stdcall  ZAux_Execute(ZMC_HANDLE  handle, const char  *pszCommand, char  *psResponse, uint32  uiResponseLength)

指令说明

发送字符串命令到控制器,缓存方式(当控制器没有缓冲时自动阻塞推荐少用,当遇到ZAux_DirectCommand不支持的Basic指令时再使用)。

输入参数

参数名

描述

handle

连接句柄。

pszCommand

发送的命令字符串。

uiResponseLength

返回的字符长度。

输出参数

参数名

描述

psResponse

返回的字符串。

返回值

成功返回值为0,非0详见错误码说明。

指令示例

在线命令函数的使用

详细说明

上位机调用上位机未封装的Basic指令功能


四、C#编写例程调试ZMC432CL-V2的脉冲闭环功能

1.通过在线命令封装脉冲闭环功能对应的上位机接口。

(1)右击【项目】→【添加】【新建项】【新建C#类】,这里新建了一个MyFullCloseLoop的C#类。

(2)查询Basic对应指令的使用说明,封装一个设置轴比例增益的上位机接口。

    /// 
    /// 设置轴的比例增益
    /// 
    /// 连接句柄
    /// 轴号
    /// 比例增益P的值
    /// 错误码
    public int ZAux_Direct_SetPGain(IntPtr handle, int iaxis, float fValue)
    {
         String cmdbuff;   //定义命令字符串
         //判断轴数是否超标
         StringBuilder cmdbuffAck = new StringBuilder(1024);
         if (iaxis > MAX_AXIS_AUX)
         {
              return ERR_AUX_PARAERR;
         }
         //生成命令,根据Basic指令的用法格式去拼接命令字符串
         cmdbuff = string.Format("P_Gain({0}) = {1}", iaxis, fValue);
         //调用命令执行函数
         return zmcaux.ZAux_DirectCommand(handle, cmdbuff, cmdbuffAck, 2048);
     }
    (3)查询Basic对应指令的使用说明,封装一个获取轴比例增益的上位机接口。
      /// 
      /// 获取轴的比例增益
      /// 
      /// 连接句柄
      /// 轴号
      /// 获取的轴比例增益P的值
      /// 错误码
      public int ZAux_Direct_GetPGain(IntPtr handle, int iaxis, ref float fValue)
      {
          String cmdbuff;  //定义命令字符串
          StringBuilder cmdbuffAck = new StringBuilder(1024);  //定义接受返回的结果字符串 
          //判断轴数是否超标
          if (iaxis > MAX_AXIS_AUX)
          {
              return ERR_AUX_PARAERR;
          }
          //生成命令  ?类似于C的printf指令,用于打印,打印出来的字符串通过cmdbuffAck去接收
          cmdbuff = string.Format("?P_Gain({0}) ", iaxis);
          //调用命令执行函数
          int iresult = zmcaux.ZAux_Execute(handle, cmdbuff, cmdbuffAck, 2048);
          if (ERR_OK != iresult)
          {
              return iresult;
          }
          //解析返回的字符串
          if (cmdbuffAck.Length == 0)
          {
              return ERR_NOACK;
          }
          else
          {
              fValue = float.Parse(cmdbuffAck.ToString());
          }
          return ERR_OK;
      }
      (4)封装好的脉冲闭环功能相关的上位机接口。

      2.C#闭环功能的测试例程的编写。
      (1)脉冲闭环测试例程界面的设计。

      (2)【连接】按钮如何连接控制器。
        int Err = 0;       //接口返回的错误码
        int LinkMode = 2;  //FastOpen接口连接类型的介绍 1-COM 2-ETH 4-PCI 5-LOCAL
        Err = zmcaux.ZAux_FastOpen(LinkMode, Buffer, 2000, out g_handle);
        if (Err == 0)
        {
            // 修改按钮文字
            LinkStatus.Text = "链接状态:OK";
            // 修改按钮背景色
            LinkStatus.BackColor = Color.FromArgb(192, 255, 192);
            //相关参数初始化
            AxisParaSet();
            //进行PID参数的初始化
            PidParaSet();
            //打开定时器
            Timer.Start();
        }
        else
        {
            // 修改按钮文字
            LinkStatus.Text = "链接状态:Ng";
            // 修改按钮背景色
            LinkStatus.BackColor = Color.FromArgb(255, 192, 192);
            //关闭定时器
            Timer.Stop();
        }
        (3)【更新PID参数】按钮如何打开和关闭脉冲闭环功能,如何更新PID参数。
          //将上位机设置的PID参数更新到控制器
          private int PidParaSet()
          {
              float TempFloat = 0;
              float TempDpos = 0, TempMpos = 0;
              bool TempInt = false;
              MyFullClosedLoop CloseLoop = new MyFullClosedLoop();
              String CompareStr = "闭环已开";
              String TempStr = IsClosedLoop.Text;
              //打开全闭环去控制轴运动
              if (TempStr == CompareStr)
              {
                  //获取轴位置,如果DPOS和MPOS相差太大不能打开脉冲闭环,保证安全
                  zmcaux.ZAux_Direct_GetDpos(g_handle, int.Parse(AxisId.Text), ref TempDpos);
                  zmcaux.ZAux_Direct_GetMpos(g_handle, int.Parse(AxisId.Text), ref TempMpos);
                  if ((TempDpos - TempMpos) > 4 || (TempDpos - TempMpos < -4))
                  {
                      Console.WriteLine("规划位置和反馈位置相差太大,无法启动闭环功能!!!!");
                      return -1;
                  }
                  //更新比例增益
                  CloseLoop.ZAux_Direct_SetPGain(g_handle, int.Parse(AxisId.Text), float.Parse(text_ParaP.Text));
                  //更新积分增益
                  CloseLoop.ZAux_Direct_SetIGain(g_handle, int.Parse(AxisId.Text), float.Parse(text_ParaI.Text));
                  //更新微分增益
                  CloseLoop.ZAux_Direct_SetDGain(g_handle, int.Parse(AxisId.Text), float.Parse(text_ParaD.Text));
                  //更新速度前馈增益
                  CloseLoop.ZAux_Direct_SetVffGain(g_handle, int.Parse(AxisId.Text), float.Parse(text_ParaVF.Text));    
                  //更新加速度前馈增益
                  CloseLoop.ZAux_Direct_SetAffGain(g_handle, int.Parse(AxisId.Text), float.Parse(text_ParaAF.Text));
                  //更新速度增益
                  CloseLoop.ZAux_Direct_SetOvGain(g_handle, int.Parse(AxisId.Text), float.Parse(text_ParaOV.Text));
                  //注意:在打开servo之前打开encoder_servo后要完成一次atype由0变为4的切换,否则会报axis:0 config not support Servo.
                  //1、先打开axis_enable 和 encoder_servo
                  zmcaux.ZAux_Direct_SetAxisEnable(g_handle, int.Parse(AxisId.Text), 1);        CloseLoop.ZAux_Direct_SetEncoderServo(g_handle, int.Parse(AxisId.Text), 1);
                  Thread.Sleep(20);
                  CloseLoop.ZAux_Direct_GetEncoderServo(g_handle, int.Parse(AxisId.Text), ref TempInt);
                  if (TempInt)
                  {
                      //2、完成一次Atype由0变为4的切换
                      zmcaux.ZAux_Direct_SetAtype(g_handle, int.Parse(AxisId.Text), 0);
                      Thread.Sleep(20);
                      zmcaux.ZAux_Direct_SetAtype(g_handle, int.Parse(AxisId.Text), 4);
                      //3、打开Servo
                      CloseLoop.ZAux_Direct_SetServo(g_handle, int.Parse(AxisId.Text), 1);
                      Thread.Sleep(10);
                      CloseLoop.ZAux_Direct_GetServo(g_handle, int.Parse(AxisId.Text), ref TempInt);
                      if (TempInt)
                      {
                          Console.WriteLine("闭环参数配置完成, 轴全闭环功能打开成功。");
                      }
                      else
                      {
                          Console.WriteLine("轴闭环开关Servo打开失败, 导致脉冲全闭环开启失败!!!");
                          return -1;
                      }
                  }
                  else
                  {
                      Console.WriteLine("轴编码器闭环EncoderServo打开失败, 导致脉冲全闭环开启失败!!!");
                      return -1;
                  }


              }
              else
              {
                  //关闭全闭环的功能
                  //1、关闭EncoderServo
                  CloseLoop.ZAux_Direct_SetEncoderServo(g_handle, int.Parse(AxisId.Text), 0);
                  Thread.Sleep(20);
                  CloseLoop.ZAux_Direct_GetEncoderServo(g_handle, int.Parse(AxisId.Text), ref TempInt);
                  if (TempInt)
                  {
                      Console.WriteLine("轴EncoderServo关闭失败!!!");
                      return -1;
                  }
                  //2、关闭EncoderServo后需要完成ATYPE的切换,保证完全关闭闭环功能
                  zmcaux.ZAux_Direct_SetAtype(g_handle, int.Parse(AxisId.Text), 0);
                  Thread.Sleep(10);
                  zmcaux.ZAux_Direct_SetAtype(g_handle, int.Parse(AxisId.Text), 4);
                  //3、关闭Servo
                  CloseLoop.ZAux_Direct_SetServo(g_handle, int.Parse(AxisId.Text), 0);
                  Thread.Sleep(20);
                  CloseLoop.ZAux_Direct_GetServo(g_handle, int.Parse(AxisId.Text), ref TempInt);
                  if (TempInt)
                  {
                      Console.WriteLine("轴Servo关闭失败!!!");
                      return -1;
                  }
              }
              return 0;
          }
          (4)【更新轴参数】按钮如何完成轴参数的更新。
            //更新轴参数
            private void AxisParaSet()
            {
                //设置最大随动误差FE_LIMIT 
                zmcaux.ZAux_Direct_SetFeLimit(g_handle, int.Parse(AxisId.Text), 500);
                //更新编码器齿轮比 (如果发N个脉冲,实际编码器反馈M个脉冲,编码器齿轮比要设置成 N/M)
                zmcaux.ZAux_Direct_EncoderRatio(g_handle, int.Parse(AxisId.Text), int.Parse(EncoderRatioMol.Text), int.Parse(EncoderRatioDenom.Text));
                //更新脉冲当量,一般脉冲当量设置成机台运动1mm需要的脉冲数
                zmcaux.ZAux_Direct_SetUnits(g_handle, int.Parse(AxisId.Text), float.Parse(text_ParaUnits.Text));
                //全闭环的功能需要把ATYPE设置成4
                zmcaux.ZAux_Direct_SetAtype(g_handle, int.Parse(AxisId.Text), 4);
                //更新速度
                zmcaux.ZAux_Direct_SetSpeed(g_handle, int.Parse(AxisId.Text), float.Parse(text_ParaSpeed.Text));
                //更新加速度、减速度
                zmcaux.ZAux_Direct_SetAccel(g_handle, int.Parse(AxisId.Text), float.Parse(text_ParaAccel.Text));
                zmcaux.ZAux_Direct_SetDecel(g_handle, int.Parse(AxisId.Text), float.Parse(text_ParaDecel.Text));
                StringBuilder Buff = new StringBuilder(512);
                //是否启用SS曲线
                if (CurveIsSS.Checked)
                {
                    //启用SS曲线,VP_MODE模式设置成7即可
                    //上位机旧库没有现成设置VP_MODE的接口,直接在线命令去封装,在线命令是万能接口
                    string CmdBuff = string.Format("VP_MODE({0}) = 7 ", int.Parse(AxisId.Text));
                    zmcaux.ZAux_DirectCommand(g_handle, CmdBuff, Buff, 512);
                }
                else
                {
                    //启用S曲线,VP_MODE模式设置成0即可
                    //上位机旧库没有现成设置VP_MODE的接口,直接在线命令去封装,在线命令是万能接口
                    string CmdBuff = string.Format("VP_MODE({0}) = 0 ", int.Parse(AxisId.Text));
                    zmcaux.ZAux_DirectCommand(g_handle, CmdBuff, Buff, 512);
                    //S曲线模式,S曲线时间sramp是有效果的,需要设置一下
                    zmcaux.ZAux_Direct_SetSramp(g_handle, int.Parse(AxisId.Text), float.Parse(text_ParaSramp.Text));
                }
            }
            (5)【手动】按钮如何控制脉冲轴的点动与寸动。
              //X-鼠标按下
              private void ButtonHangRev_MouseDown(object sender, MouseEventArgs e)
              {
                  if (IsInchMode.Checked)
                  {
                      //寸动运动
                      zmcaux.ZAux_Direct_Single_Move(g_handle, int.Parse(AxisId.Text), -1 * float.Parse(InchDis.Text));
                  }
                  else
                  {
                      //手动运动
                      zmcaux.ZAux_Direct_Single_Vmove(g_handle, int.Parse(AxisId.Text), -1);
                  }
              }
              //X-鼠标松开
              private void ButtonHangRev_MouseUp(object sender, MouseEventArgs e)
              {
                  if (IsInchMode.Checked == false)
                  {
                      //手动运动停止
                      zmcaux.ZAux_Direct_Single_Cancel(g_handle, int.Parse(AxisId.Text), 2);
                  }
              }
              //X+鼠标按下
              private void ButtonHangFwd_MouseDown(object sender, MouseEventArgs e)
              {
                  if (IsInchMode.Checked)
                  {
                      //寸动运动
                      zmcaux.ZAux_Direct_Single_Move(g_handle, int.Parse(AxisId.Text), 1 * float.Parse(InchDis.Text));
                  }
                  else
                  {
                      //手动运动
                      zmcaux.ZAux_Direct_Single_Vmove(g_handle, int.Parse(AxisId.Text), 1);
                  }
              }
              //X+鼠标松开
              private void ButtonHangFwd_MouseUp(object sender, MouseEventArgs e)
              {
                  if (IsInchMode.Checked == false)
                  {
                      //手动运动停止
                      zmcaux.ZAux_Direct_Single_Cancel(g_handle, int.Parse(AxisId.Text), 2);
                  }
              }

              五、通过RTSys的示波器对比开环控制和全闭环控制的情况

              示波器的使用可以参考正运动小助手的历史推文《运动控制看的更清楚细致!RTSys示波器功能简介 (qq.com)》。

              1.开环控制情况分析

              测试发现:步进驱动器的开环控制,运动过程中随动误差(规划位置和光栅尺反馈位置的差值)一直维持在0.02个用户单位左右(这里一个用户单位即一个UNITS设置的是1mm),当运动结束时光栅尺的反馈位置和指令规划位置也不相等,大概差了0.0015个用户单位,折算为脉冲数是0.0015*用户单位=3个脉冲

              2.闭环控制情况分析

              测试发现:步进驱动器的闭环控制,运动过程中随动误差(规划位置和光栅尺反馈位置的差值)除了启动和停止以外大部分保持在0个脉冲当量左右,相比较开环控制有较大的提升,当运动结束时光栅尺的反馈位置和指令规划位置也是相等的。

              六、总结

              1.启用控制器闭环的时候注意要在打开encoder_servo后,打开servo之前要完成一次ATYPE从0到4的切换,这样才可以正常打开控制器闭环的功能。
              2.启用控制器闭环同时还需要打开单轴使能axis_enable,这样才能保证控制器闭环的正常启用。

              3.为保证控制器闭环功能的完全关闭,在关闭ENCODER_SERVO后需要完成一次 ATYPE从0到4的切换,这样才能保证控制器闭环功能完全关闭。

              4.教学视频。

              以下视频来源于
              正运动小助手

              完整代码获取地址

              本次,正运动技术步进的光栅尺全闭环EtherCAT运动控制器ZMC432CL-V2(三):C#编程调试,就分享到这里。

              免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

              本帖子中包含更多资源

              您需要 登录 才可以下载或查看,没有账号?立即注册

              x
              您需要登录后才可以回帖 登录 | 立即注册

              本版积分规则

              上一主题上一主题         下一主题下一主题
              QQ手机版小黑屋粤ICP备17165530号

              Copyright 2010-2015. All rights reserved. 

              微信公众号二维码 抖音二维码 百家号二维码 今日头条二维码哔哩哔哩二维码