现在越来越多的非标设备使用的是运动控制卡,那什么是运动控制卡?
运动控制卡是一种基于PC机及工业PC机、 用于各种运动控制场合(包括位移、速度、加速度等)的上位控制单元。
所以运动控制卡的编程就需要用到高级语言,常用的有C++,VB.NET,Labview,C#这几种
下面高级语言以C#为例,运动控制卡以固高GT-400-G-PCI(4轴控制卡)为例
常见的系统架构
一控制卡组成
1.控制卡
控制卡需要插在PC的PCI插槽内,露出的接口CN17需要用排线和外部端子板连接
运动控制卡
2.端子板
端子板一般装在配电盘上,用于接线,
下图中左边的四个AXIS就是轴系的脉冲接口
右边为IO和限位接线端子
最上门的CN17需要用排线连接到PC内的运动控制卡
端子板
二.配置运动控制卡(固高官网下载地址:)
1.首先装好驱动程序,驱动包可以找供应商拿
2.配置卡的参数
安装好驱动后,打开固高配置软件,主要配置为伺服的脉冲模式,正负极限的设置,急停的设置
当中细节太多这边不一一讲
3.用demo控制轴运动
配置好参数后,用配置软件动一下轴确定轴参数都对后才能用上位机控制
轴一的配置界面
轴控制
三.用C#编程
1.新建一个winform窗体程序,把固高的动态链接库拷贝进去(正常和供应商索取驱动还有相关资料)
动态链接库还有文件
2.程序内引用gts文件
右击项目?添加?现有项?选择刚才拷贝的文件
添加完效果
3.开始编程
卡操作流程:初始化?读取参数配置(就是用DEMO软件配置好的参数)?按逻辑控制每个轴归零?程序控制轴进行位移运动
我们在新建一个GtsCard类,把gts类中提供的方法重新整理下,方便以后复用
//固高运动控制卡 public class GtsCard { /// /// 初始化,加载配置,清除轴系报错 /// /// /// /// /// public bool Initial(gtsCardID gtsCard, Axis[] axis) { short result; bool isOk = false; try { //channel : 打开运动控制器的方式。默认值为:0。0:正常打开运动控制器。1:内部调试方式,用户不能使用。 //param : 当channel=1时,该参数有效。 result = mc.GT_Open(gtsCard.cardNum, 0, 1); if (0 == result) { result += mc.GT_Reset(gtsCard.cardNum); result += mc.GT_LoadConfig(gtsCard.cardNum, gtsCard.filePath); for (int i = 0; i < axis.Length; i++) { result += mc.GT_ClrSts(gtsCard.cardNum, (short)(axis[i].AxisId + 1), 1); result += mc.GT_ZeroPos(gtsCard.cardNum, (short)(axis[i].AxisId + 1), 1); result += mc.GT_SetAxisBand(gtsCard.cardNum, (short)(axis[i].AxisId + 1), axis[i].Band, 100);//设置到位脉冲检测,规划器停止100ms后检测 } if (0 == result) { isOk = true; } else { isOk = false; } } else { isOk = false; } } catch (System.Exception ex) { isOk = false; } return isOk; }//回零线程 Task HomeMove(gtsCardID gtsCard, Axis axis) { return new Task(() => { try { mc.THomePrm HomePrm = new mc.THomePrm(); mc.TTrapPrm trapPrm = new mc.TTrapPrm(); mc.THomeStatus pHomeStatus = new mc.THomeStatus(); bool isOk = false; int result; int pValue, nValue, hValue; bool plimt = false;bool nlimt = false; bool hlimt=false; result = mc.GT_ClrSts(gtsCard.cardNum, (short)(axis.AxisId + 1), 1);//错误清除 Servo_On_Axis(gtsCard, axis); result += mc.GT_ZeroPos(gtsCard.cardNum, (short)(axis.AxisId + 1), 1); //脉冲清零 result += mc.GT_GetTrapPrm(gtsCard.cardNum, (short)(axis.AxisId + 1), out trapPrm);//获取点位运动参数 result += mc.GT_GetDi(0, mc.MC_LIMIT_POSITIVE, out pValue);//以未取反p开关为例 result += mc.GT_GetDi(0, mc.MC_LIMIT_NEGATIVE, out nValue);//以未取反n开关为例 result += mc.GT_GetDi(0, mc.MC_HOME, out hValue);//以未取反home开关为例 if ((1 << (axis.AxisId) & pValue) == 1) plimt = true; if((1 << (axis.AxisId) & nValue) == 1) nlimt = true; if ((1 << (axis.AxisId) & hValue) == 1) hlimt = true; if(plimt||nlimt||hlimt) { pValue = mc.GT_PrfTrap(gtsCard.cardNum, (short)(axis.AxisId + 1));//设置点位模式 result = mc.GT_GetTrapPrm(gtsCard.cardNum, (short)(axis.AxisId + 1), out trapPrm);//获取点位运动参数 trapPrm.acc = 1; trapPrm.dec = 1; trapPrm.smoothTime = 30; result = mc.GT_SetTrapPrm(gtsCard.cardNum, (short)(axis.AxisId + 1), ref trapPrm);//设置点位参数 result = mc.GT_SetVel(gtsCard.cardNum, (short)(axis.AxisId + 1), 5);//设置小段位移速度 if(plimt || hlimt) result = mc.GT_SetPos(gtsCard.cardNum, (short)(axis.AxisId + 1), -1000);//设置小段位移距离大于Home开关宽度。+-代表方向 if(nlimt) result = mc.GT_SetPos(gtsCard.cardNum, (short)(axis.AxisId + 1), 1000);//设置小段位移距离大于Home开关宽度。+-代表方向 result = mc.GT_Update(gtsCard.cardNum, 1 << ((axis.AxisId + 1) - 1));//启动小段位移 } //home开关触发,即当前位置在home点轴系就离开 { result += mc.GT_GetHomePrm(gtsCard.cardNum, (short)(axis.AxisId + 1), out HomePrm);//获取回零运动参数 switch ((short)axis.HomeMode) { case 0: HomePrm.mode = mc.HOME_MODE_LIMIT;//找极限 break; case 1: HomePrm.mode = mc.HOME_MODE_LIMIT_HOME;//找极限找原点 break; case 2: HomePrm.mode = mc.HOME_MODE_LIMIT_INDEX;//找极限再找Z相 break; case 3: HomePrm.mode = mc.HOME_MODE_LIMIT_HOME_INDEX;//找极限找原点找Z相 break; case 4: HomePrm.mode = mc.HOME_MODE_HOME;//找原点 break; case 5: HomePrm.mode = mc.HOME_MODE_HOME_INDEX;//找原点找Z相 break; case 6: HomePrm.mode = mc.HOME_MODE_INDEX;//找Z相 break; } switch ((short)axis.HomeDir) { case 0: HomePrm.moveDir = -1; break; case 1: HomePrm.moveDir = 1; break; } HomePrm.edge = 0; HomePrm.velHigh = (double)axis.HomeVel * 0.001;//换算脉冲每秒 HomePrm.velLow = 5; HomePrm.acc = axis.HomeAcc * 0.000001;//换算成脉冲数每秒 HomePrm.dec = axis.HomeAcc * 0.000001; HomePrm.smoothTime = 10; HomePrm.homeOffset = axis.HomeOffset; HomePrm.searchHomeDistance = 0; HomePrm.searchIndexDistance = 20000;//寻找Z相信号的距离 HomePrm.escapeStep = 20000; } //设置回零参数 result += mc.GT_GoHome(gtsCard.cardNum, (short)(axis.AxisId + 1), ref HomePrm);//启动SmartHome回原点 DateTime start = DateTime.Now; while (true) { mc.GT_GetHomeStatus(gtsCard.cardNum, (short)(axis.AxisId + 1), out pHomeStatus); if (pHomeStatus.run== 0) // 等待搜索原点停止 { Thread.Sleep(100); if (pHomeStatus.stage == 100) {//等待搜索原点是否结束 Thread.Sleep(2000); result += mc.GT_ZeroPos(gtsCard.cardNum, (short)(axis.AxisId + 1), 1); if (result == 0) isOk = true; break; } } TimeSpan span = DateTime.Now - start; if (span.Seconds > 40) { EmgStop(gtsCard,axis); isOk = false; break; } } return isOk; } catch (Exception e) { return false; } }); }//jog运动 public void Jog_Move(gtsCardID gtsCard, Axis axis, bool JogOn) { if (JogOn) { double jogvel; short sRtn; mc.TJogPrm tJogPrm = new mc.TJogPrm { acc = axis.JogAcc* 0., dec = axis.JogDec * 0., smooth =axis.S_Factor }; sRtn = mc.GT_PrfJog(gtsCard.cardNum, (short) (axis.AxisId+1));//指定轴为JOG运动模式 sRtn += mc.GT_SetJogPrm(gtsCard.cardNum, (short)(axis.AxisId + 1), ref tJogPrm);//设置jog运动参数 if (axis.jogDir == 0) jogvel = - axis.JogVel * 0.001; else jogvel = axis.JogVel * 0.001; sRtn += mc.GT_SetVel(gtsCard.cardNum, (short)(axis.AxisId + 1), jogvel);//设置目标速度,velJd的符号决定JOG运动方向 sRtn += mc.GT_Update(gtsCard.cardNum, 1 << (short)(axis.AxisId + 1)-1);//更新轴运动,按bit跟新轴的运动,0为1号轴 } else { EmgStop(gtsCard, axis); } }//绝对位移 public bool Absolute_Move(gtsCardID gtsCard, Axis axis, int Move_Pulse, int Speed) { try { short sRtn;//返回值 mc.TTrapPrm trap = new mc.TTrapPrm(); sRtn = mc.GT_PrfTrap(gtsCard.cardNum, (short)(axis.AxisId + 1)); //设置为点位运动,模式切换需要停止轴运动。 //若返回值为 1:若当前轴在规划运动,请调用GT_Stop停止运动再调用该指令。 sRtn += mc.GT_GetTrapPrm(gtsCard.cardNum, (short)(axis.AxisId + 1), out trap); /*读取点位运动参数(不一定需要)。若返回值为 1:请检查当前轴是否为 Trap 模式 若不是,请先调用 GT_PrfTrap 将当前轴设置为 Trap 模式。*/ trap.acc = axis.MoveAcc * 0.; //单位pulse/ms2换成脉冲/秒 trap.dec = axis.MoveDec * 0.; //单位pulse/ms2换成脉冲/秒 trap.velStart = 0; //起跳速度,默认为0。 trap.smoothTime = (short)axis.S_Factor; //平滑时间,使加减速更为平滑。范围[0,50]单位ms。 sRtn += mc.GT_SetTrapPrm(gtsCard.cardNum, (short)(axis.AxisId + 1), ref trap);//设置点位运动参数。 sRtn += mc.GT_SetVel(gtsCard.cardNum, (short)(axis.AxisId + 1), (double)Speed * 0.001); //设置目标速度 sRtn += mc.GT_SetPos(gtsCard.cardNum, (short)(axis.AxisId + 1), Move_Pulse); //设置目标位置 sRtn += mc.GT_Update(gtsCard.cardNum, 1 << ((short)(axis.AxisId + 1) - 1)); //更新轴运动 if (sRtn == 0) return true; else return false; }catch { return false; } }//使能 public bool Servo_On_Axis(gtsCardID gtsCard, Axis axis) { mc.GT_ClrSts(gtsCard.cardNum,(short) (axis.AxisId+1), 1); int enableStatus; short ret = mc.GT_GetDo(gtsCard.cardNum, mc.MC_ENABLE, out enableStatus); if ((1 << (axis.AxisId) & enableStatus) == 0) mc.GT_AxisOn(gtsCard.cardNum, (short)(axis.AxisId + 1)); Thread.Sleep(100); ret = mc.GT_GetDo(gtsCard.cardNum, mc.MC_ENABLE, out enableStatus); if ((1 << (axis.AxisId) & enableStatus) == 1) return true; else return false; }//设置Do public bool Write_Do(gtsCardID gtsCard, DiDoStruct doData, short doValue) { if (doValue == 0)//固高0为低电平有效,1为高电平有效,此处反转,doValue写入1时打开输出, doValue = 1; else doValue = 0; int dovalue = 0; short result = mc.GT_GetDo(gtsCard.cardNum, mc.MC_GPO, out dovalue); if (((1 << doData.channel) & dovalue) != doValue)//如果设置和读取的状态不一致就写入 { mc.GT_SetDoBit(gtsCard.cardNum, mc.MC_GPO, (short)(doData.channel + 1), doValue); } result = mc.GT_GetDo(gtsCard.cardNum, mc.MC_GPO, out dovalue); if (((1 << doData.channel) & dovalue) == doValue)//检测状态是否写入成功 { return true; } else { return false; } }//读取Di public bool Read_All_Input(gtsCardID gtsCard, out int[] diData) { diData = new int[16]; int divalue = 0; short result = mc.GT_GetDi(gtsCard.cardNum, mc.MC_GPI, out divalue); if (0 == result) { for (int i = 0; i < diData.Length; i++) { if (((1 << i) & divalue) == 0) diData[i] = 1; else diData[i] = 0; } } else { return false; } return true; }}//轴参数 public struct Axis { public int BoardId; //Board ID public int AxisId; //轴ID public int Rate; //脉冲与实际位置换算比例 public int Band; //诊断到位与否的误差脉冲数 public double S_Factor;//S曲线运动 public double HomeVel; public double HomeAcc; public int HomeOffset; /// /// 固高卡回零方式0找极限,1找原点 /// public int HomeMode; public int HomeDir; public int HomeShift;//回零偏移 public int MoveVel; public double MoveAcc; public double MoveDec; public double JogVel; public double JogAcc; public double JogDec; public int jogDir; public string ParPaath; }//卡参数 public struct gtsCardID { public short cardNum; public string filePath; //配置文件存储路径 }
4.调用初始化方法,其余控制操作流程一样.
5.读取IO状态
因为PC和PLC有区别,刷新IO需要自己单独写一个线程一直刷新,或者用timer控件,正常图简单都是这样写
//线程中一直刷新就行,didata,dodata要用全局变量private void UpdateStatusTask() { Task task = Task.Run(() => { while (true) { int[] didata = new int[16]; int[] dodata = new int[16]; gtsCard.Read_All_Input(settingConfig.gtsCard, out didata); gtsCard.Read_All_DoValue(settingConfig.gtsCard, out dodata); } }); task.ContinueWith(error => { string Exception = Convert.ToString(task.Exception); MessageBox.Show("UpdateTask线程出现异常::" + Exception, "LogicControl--Message", MessageBoxButtons.OK, MessageBoxIcon.Error); WriteLog("UpdateTask线程出现异常::" + Exception); },TaskContinuationOptions.OnlyOnFaulted); }
总结:C#简单的控制运动控制卡,需要的C#知识并不是很多,掌握基础,线程,委托,还有常用控件的使用就差不多了,可以写出一般的程序.更进一步还是需要不断的学习