本節主要講解如何通過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)將zauxdll.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示波器功能簡介 (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系列運動控制卡等。
|