//www.thesjoquists.com 2025-02-27 16:25 深圳市正运动技术有限公司
本节主要讲解如何通过C#编写程序调试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产品介绍视频点击→步进控制的光栅尺全闭环解决方案:32轴EtherCAT总线运动控制器ZMC432CL-V2。
更多关于ZMC432CL-V2详情介绍点击→步进控制的光栅尺全闭环解决方案:32轴EtherCAT总线运动控制器ZMC432CL-V2。
(一)新建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)将zauxdll.dll和zmotion.dll文件放入bin\debug文件夹中。
5.用vs打开新建的项目文件,在右边的解决方案资源管理器中点击显示所有文件,然后鼠标右击Zmcaux.cs文件,点击包括在项目中。
6.双击Form1.cs里面的Form1,出现代码编辑界面,在文件开头写入using cszmcaux,并声明控制器句柄g_handle。
1.PC函数手册可在光盘资料查看,具体路径如下。
2.控制器/卡接口之链接控制器,获取链接句柄。
3.万能指令之在线命令。
有一些使用频率较低的Basic指令我们没有封装到上位机的辅助库中,如果用户上位机需要调用对应的Basic指令的话,可以通过在线命令自行进行相关指令封装。
1.通过在线命令封装脉冲闭环功能对应的上位机接口。
(1)右击【项目】→【添加】→【新建项】→【新建C#类】,这里新建了一个MyFullCloseLoop的C#类。
(2)查询Basic对应指令的使用说明,封装一个设置轴比例增益的上位机接口。
/// <summary> /// 设置轴的比例增益 /// </summary> /// <param name="handle">连接句柄</param> /// <param name="iaxis">轴号</param> /// <param name="fValue">比例增益P的值</param> /// <returns>错误码</returns> 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对应指令的使用说明,封装一个获取轴比例增益的上位机接口。
/// <summary> /// 获取轴的比例增益 /// </summary> /// <param name="handle">连接句柄</param> /// <param name="iaxis">轴号</param> /// <param name="fValue">获取的轴比例增益P的值</param> /// <returns>错误码</returns> 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示波器功能简介 (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#编程调试”查看。
完整代码获取地址
▼
本次,正运动技术步进的光栅尺全闭环EtherCAT运动控制器ZMC432CL-V2(三):C#编程调试,就分享到这里。
更多精彩内容请关注“正运动小助手”公众号,需要相关开发环境与例程代码,请咨询正运动技术销售工程师:400-089-8936。
本文由正运动技术原创,欢迎大家转载,共同学习,一起提高中国智能制造水平。文章版权归正运动技术所有,如有转载请注明文章来源。
正运动技术专注于运动控制技术研究和通用运动控制软硬件产品的研发,是国家级高新技术企业。正运动技术汇集了来自华为、中兴等公司的优秀人才,在坚持自主创新的同时,积极联合各大高校协同运动控制基础技术的研究,是国内工控领域发展最快的企业之一,也是国内少有、完整掌握运动控制核心技术和实时工控软件平台技术的企业。主要业务有:运动控制卡_运动控制器_EtherCAT运动控制卡_EtherCAT控制器_运动控制系统_视觉控制器__运动控制PLC_运动控制_机器人控制器_视觉定位_XPCIe/XPCI系列运动控制卡等。