插补运动是轨迹加工必不可少的运动方式,在实际应用中复杂曲线通常需要离散成小线段。固高运动控制器插补运动的缓冲区只有4096段空间,那么问题来了,超过4096段数据要怎么处理,很多朋友首先想到的可能是先跑完一部分,在压剩余的数据继续跑。如果是这样处理,在数据量很多时就要分几次跑,当然就会出现跑跑停停的现象。
那么,如何处理才比较好?通常是使用一边跑一边压数据的方式。假设现在要跑下图的轨迹,轨迹离散成1万多段数据,下面我来举例说明如何实现边跑边压数据。
运动轨迹
1.建立坐标系
public static void CreateCrd(short cardNum, short crd, short dimension, short[] crdAxis, int[] originPos)
{
short sRtn = 0;
// 建立号坐标系,设置坐标系参数
gts.mc.TCrdPrm crdPrm = new gts.mc.TCrdPrm();
sRtn = gts.mc.GT_GetCrdPrm(cardNum, crd, out crdPrm);
WriteLog.commandhandler("GTN_GetCrdPrm", sRtn);
crdPrm.dimension = dimension; // 坐标系为二维坐标系
crdPrm.synVelMax = 500; // 最大合成速度: pulse/ms
crdPrm.synAccMax = 1000; // 最大加速度: pulse/ms^2
crdPrm.evenTime = 50; // 最小匀速时间: ms
crdPrm.setOriginFlag = 1; // 指定坐标系的原点坐标的规划位置
switch (crdAxis[0])
{
case 1:
crdPrm.profile1 = 1;
crdPrm.originPos1 = originPos[0];
break;
case 2:
crdPrm.profile2 = 1;
crdPrm.originPos2 = originPos[0];
break;
case 3:
crdPrm.profile3 = 1;
crdPrm.originPos3 = originPos[0];
break;
case 4:
crdPrm.profile4 = 1;
crdPrm.originPos4 = originPos[0];
break;
case 5:
crdPrm.profile5 = 1;
crdPrm.originPos5 = originPos[0];
break;
case 6:
crdPrm.profile6 = 1;
crdPrm.originPos6 = originPos[0];
break;
case 7:
crdPrm.profile7 = 1;
crdPrm.originPos7 = originPos[0];
break;
case 8:
crdPrm.profile8 = 1;
crdPrm.originPos8 = originPos[0];
break;
default: break;
}
switch (crdAxis[1])
{
case 1:
crdPrm.profile1 = 2;
crdPrm.originPos1 = originPos[1];
break;
case 2:
crdPrm.profile2 = 2;
crdPrm.originPos2 = originPos[1];
break;
case 3:
crdPrm.profile3 = 2;
crdPrm.originPos3 = originPos[1];
break;
case 4:
crdPrm.profile4 = 2;
crdPrm.originPos4 = originPos[1];
break;
case 5:
crdPrm.profile5 = 2;
crdPrm.originPos5 = originPos[1];
break;
case 6:
crdPrm.profile6 = 2;
crdPrm.originPos6 = originPos[1];
break;
case 7:
crdPrm.profile7 = 2;
crdPrm.originPos7 = originPos[1];
break;
case 8:
crdPrm.profile8 = 2;
crdPrm.originPos8 = originPos[1];
break;
default: break;
}
if (dimension > 2)
{
switch (crdAxis[2])
{
case 1:
crdPrm.profile1 = 3;
crdPrm.originPos1 = originPos[2];
break;
case 2:
crdPrm.profile2 = 3;
crdPrm.originPos2 = originPos[2];
break;
case 3:
crdPrm.profile3 = 3;
crdPrm.originPos3 = originPos[2];
break;
case 4:
crdPrm.profile4 = 3;
crdPrm.originPos4 = originPos[2];
break;
case 5:
crdPrm.profile5 = 3;
crdPrm.originPos5 = originPos[2];
break;
case 6:
crdPrm.profile6 = 3;
crdPrm.originPos6 = originPos[2];
break;
case 7:
crdPrm.profile7 = 3;
crdPrm.originPos7 = originPos[2];
break;
case 8:
crdPrm.profile8 = 3;
crdPrm.originPos8 = originPos[2];
break;
default: break;
}
}
if (dimension == 4)
{
switch (crdAxis[3])
{
case 1:
crdPrm.profile1 = 4;
crdPrm.originPos1 = originPos[3];
break;
case 2:
crdPrm.profile2 = 4;
crdPrm.originPos2 = originPos[3];
break;
case 3:
crdPrm.profile3 = 4;
crdPrm.originPos3 = originPos[3];
break;
case 4:
crdPrm.profile4 = 4;
crdPrm.originPos4 = originPos[3];
break;
case 5:
crdPrm.profile5 = 4;
crdPrm.originPos5 = originPos[3];
break;
case 6:
crdPrm.profile6 = 4;
crdPrm.originPos6 = originPos[3];
break;
case 7:
crdPrm.profile7 = 4;
crdPrm.originPos7 = originPos[3];
break;
case 8:
crdPrm.profile8 = 4;
crdPrm.originPos8 = originPos[3];
break;
default: break;
}
}
sRtn = gts.mc.GT_SetCrdPrm(cardNum, crd, ref crdPrm);
WriteLog.commandhandler("GTN_SetCrdPrm", sRtn);
sRtn = gts.mc.GT_CrdClear(cardNum, crd, 0); // 清除坐标系 1 的 FIFO0 中的数据
WriteLog.commandhandler("GT_CrdClear", sRtn);
}
2.压入插补数据并启动
这里我是先把坐标从txt文件中读取存在列表中,再循环压数据的过程中,当到达某个数量时就启动插补,然后继续一边跑一遍压剩余的数据。在这里有一点需要特别提醒一下,由于跑的是小线段,所以在使用时,尤其是运动速度比较高时,压下去的数据很快就跑完,所以要保证继续压数据的效率。因此要保证线程的优先级,避免缓冲区跑空。关于线程优先级自行研究,这里只是做个提示。
public static void CrdRunAndLoadData(short cardNum,short crd,short lsrChn,double vel,double acc,bool lookAheadFlag,string fileName,int startNum)
{
short sRtn=0;
uint dataCount=0;
int space;
bool startFlag = false;
TCrdPos crdPos = new TCrdPos();
crdPos.xPos = new List<double>();
crdPos.yPos = new List<double>();
crdPos.zPos = new List<double>();
crdPos.rPos = new List<double>();
crdPos.laserSwitch = new List<short>();
string[] strArray = File.ReadAllLines(fileName);
for (long i = 0; i < strArray.Length; i++)
{
string[] str = strArray[i].Split(',');
crdPos.xPos.Add(double.Parse(str[0]));
crdPos.yPos.Add(double.Parse(str[1]));
crdPos.rPos.Add(0);
crdPos.zPos.Add(0);
crdPos.laserSwitch.Add(short.Parse(str[2]));
dataCount++;
crdPos.dataCount = dataCount;
}
CrdLineXYG02D(cardNum, crd, crdPos.xPos[0], crdPos.yPos[0], vel, acc, lookAheadFlag);
for(int i=0;i< crdPos.dataCount;i++)
{
if (crdPos.laserSwitch[i] == 1 && lastLaserOn == false)
{
sRtn=BuflaserOn(cardNum, crd, lsrChn, lookAheadFlag);
if (1 == sRtn)
{
do
{
// 查询运动缓存区空间,直至空间不为
sRtn = gts.mc.GT_CrdSpace(cardNum, crd, out space, 0);
} while (0 == space);
// 重新调用上次失败的插补指令
sRtn = BuflaserOn(cardNum, crd, lsrChn, lookAheadFlag);
}
lastLaserOn = true;
}
else if (crdPos.laserSwitch[i] == 0 && lastLaserOn == true)//指令要关光,原来是开光状态,需要关闭激光
{
//插入关光指令
sRtn = BuflaserOff(cardNum, crd, lsrChn, lookAheadFlag);
if (1 == sRtn)
{
do
{
// 查询运动缓存区空间,直至空间不为
sRtn = gts.mc.GT_CrdSpace(cardNum, crd, out space, 0);
} while (0 == space);
// 重新调用上次失败的插补指令
sRtn = BuflaserOff(cardNum, crd, lsrChn, lookAheadFlag);
}
lastLaserOn = false;
}
if (i == startNum)
{
int temp = 0x1;
int ltemp = crd - 1;
int opCrd = temp << ltemp;
// 启动运动
sRtn = gts.mc.GT_CrdStart(cardNum, (short)opCrd, 0);
WriteLog.commandhandler("GTN_CrdStart1", sRtn);
if(sRtn==0)
{
startFlag = true;
WriteLog.LogHandler("插补运动开始");
}
}
sRtn = CrdLineXY2D(cardNum, crd, crdPos.xPos[i], crdPos.yPos[i], vel, acc, lookAheadFlag);
if (1 == sRtn)
{
do
{
// 查询运动缓存区空间,直至空间不为
sRtn = gts.mc.GT_CrdSpace(cardNum, crd, out space, 0);
//commandhandler("GTN_CrdSpace", sRtn, false);
} while (0 == space);
// 重新调用上次失败的插补指令
sRtn = CrdLineXY2D(cardNum, crd, crdPos.xPos[i], crdPos.yPos[i], vel, acc, lookAheadFlag);
}
}
if (lastLaserOn == true)
{
sRtn = BuflaserOff(cardNum, crd, lsrChn, lookAheadFlag);
lastLaserOn = false;
}
if(lookAheadFlag)
{
do
{
sRtn = gts.mc_la.GT_CrdDataEx(cardNum, crd, System.IntPtr.Zero, 0);
WriteLog.commandhandler("GTN_CrdDataEx", sRtn);
} while (sRtn != 0);
}
if (startFlag == false)
{
int temp = 0x1;
int ltemp = crd - 1;
int opCrd = temp << ltemp;
// 启动运动
sRtn = gts.mc.GT_CrdStart(cardNum, (short)opCrd, 0);
WriteLog.commandhandler("GTN_CrdStart2", sRtn);
if (sRtn == 0)
{
startFlag = true;
WriteLog.LogHandler("插补运动开始");
}
}
short crdRun;
int seg;
do
{
GTSControl.GetCrdStatus(cardNum, crd, out crdRun, out seg);
} while (crdRun == 1);
WriteLog.LogHandler("插补运动结束");
}
3.在线程中执行插补运动
private void CrdMove(object obj)
{
string filePath= (string)obj;
GetLasrPrm();
GetCrdPrm();
GTSControl.InitLaser(cardNum, lsrMode, lsrChn, maxPower, minPower, frqOrwidth, power);
bool lookAhead = checkBox_IsLookAhead.Checked == true ? true : false;
short[] crdAxis = new short[2] { axisX,axisY};
int[] originPos = new int[2] { 0, 0 };
GTSControl.CreateCrd(cardNum, crdNum, dmsNum, crdAxis, originPos);
if(lookAhead)
{
gts.mc_la.TLookAheadParameter lookAheadPara = new gts.mc_la.TLookAheadParameter();
lookAheadPara.time = lookAheadTime;
lookAheadPara.radiusRatio = lookAheadRadius;
lookAheadPara.lookAheadNum = 200;
lookAheadPara.aMax1 = 3000;
lookAheadPara.aMax2 = 3000;
lookAheadPara.aMax3 = 3000;
lookAheadPara.vMax1 = 500;
lookAheadPara.vMax2 = 500;
lookAheadPara.vMax3 = 500;
lookAheadPara.DVMax1 = 500;
lookAheadPara.DVMax2 = 500;
lookAheadPara.DVMax3 = 500;
lookAheadPara.axisRelation1 = 1;
lookAheadPara.axisRelation2 = 2;
lookAheadPara.axisRelation3 = 3;
lookAheadPara.scale1 = axisScale;
lookAheadPara.scale2 = axisScale;
lookAheadPara.scale3 = axisScale;
GTSControl.initLookAhead(cardNum, crdNum, axisScale, ref lookAheadPara);
}
GTSControl.CrdRunAndLoadData(cardNum, crdNum, lsrChn, crdVel, crdVel, lookAhead, filePath, 3000);
}
private void btn_start_Click(object sender, EventArgs e)
{
//启动插补运动
if (!string.IsNullOrEmpty(fileName))
{
try
{
Thread thread = new Thread(new ParameterizedThreadStart(CrdMove));
thread.Priority = ThreadPriority.Highest;
thread.Start(fileName); // 使用一个包含两个字符串的数组作为参数
}
catch (Exception ex)
{
//MessageBox.Show("Error occurred: " + ex.Message);
MessageBox.Show("文件有错!" + ex.Message);
//Environment.Exit(1);
}
}
}
需要完整Demo请在下方下载。

我也是有底线哒~
© 版权声明
THE END
暂无评论内容