上节课程给大家介绍了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产品介绍视频可点击→“步进控制的光栅尺全闭环解决方案:32轴EtherCAT总线运动控制器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.控制器/卡接口之链接控制器,获取链接句柄。
3.万能指令之在线命令。
有一些使用频率较低的Basic指令我们没有封装到上位机的辅助库中,如果用户上位机需要调用对应的Basic指令的话,可以通过在线命令自行进行相关指令封装。
四、C#编写例程调试ZMC432CL-V2的脉冲闭环功能
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的示波器对比开环控制和全闭环控制的情况
示波器的使用可以参考正运动小助手的历史推文《运动控制看的更清楚细致!RTSys示波器功能简介》。
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#编程调试,就分享到这里。
本文由正运动技术原创,欢迎大家转载,共同学习,一起提高中国智能制造水平。文章版权归正运动技术所有,如有转载请注明文章来源。