Today, ZMotion shares customized motion curve application of EtherCAT motion control card, exactly, "how to encapsulate Basic instructions that you want to use into upper computer interface through online command" will be introduced.
01 Hardware Motion Control Card Introduction
ECI2828 motion control card is one bus type and modular network motion control card. As for programming method, it is developed through VC, VB, VS, C++ and several kinds of advanced languages. And there needs the dynamic library "zmotion.dll" for program running.
02 Qt development of motion control card
(1) Build new Qt project
--Copy files that relate to function library to new built project.
--Add static library of function library into new built project. (zmotion.lib)
--Add head files that relate to function library to project (zmcaux.cpp 、 zmcaux.h 、 Zmotion.h)
--Declare relevant head files, and define link handle.
(2) PC function introduction
--Obtain PC function library manual from "Zmotion/Download" or "Contact us".
--For PC programming, there needs to build the connection between upper computer and controller. Like motion control card connection, use net port, details as follow:
--If you want to encapsulate Basic instruction as upper computer, the interface that can be directly called must use "online command" interface to do function encapsulation. Below is online command interface description.
--Encapsulation example: SPEED of Basic instruction.
(3) Qt does encapsulation of Move_Pt instruction to achieve motion of custom curve
--Qt interface of customized curve as follow:
--Bind one channel function with Click Event of [connect] connect button through Qt to do controller connection.
//define one timer
QTimer *UpData = new QTimer(this);
connect(UpData,&QTimer::timeout,this,[=](){
if(g_handle!=0)
{
//obtain axis position information
ZAux_Direct_GetAllAxisPara(g_handle,"DPOS",AxisNum,Dpos);
ui->DposX->setText(QString("%1").arg(Dpos[0]));
ui->DposY->setText(QString("%1").arg(Dpos[1]));
ui->DposZ->setText(QString("%1").arg(Dpos[2]));
ui->DposU->setText(QString("%1").arg(Dpos[3]));
//obtain axis speed information
ZAux_Direct_GetAllAxisPara(g_handle,"MSPEED",AxisNum,Mspeed);
ui->MspeedX->setText(QString("%1").arg(Mspeed[0]));
ui->MspeedY->setText(QString("%1").arg(Mspeed[1]));
ui->MspeedZ->setText(QString("%1").arg(Mspeed[2]));
ui->MspeedU->setText(QString("%1").arg(Mspeed[3]));
//obtain motion situation of each axis
float idle[4]={0};
ZAux_Direct_GetAllAxisPara(g_handle,"idle",AxisNum,idle);
idle[0]=idle[0]+idle[1]+idle[2]+idle[3];
if(idle[0]>(-4))
{
MotionStatus=1;//while moving
}
else
{
MotionStatus=0;
}
}
}
-- Update position and speed message of each axis through timer.
//define one timer
QTimer*UpData =newQTimer(this);
connect(UpData,&QTimer::timeout,this,[=](){
if(g_handle!=0)
{
//obtain axis position information
ZAux_Direct_GetAllAxisPara(g_handle,"DPOS",AxisNum,Dpos);
ui->DposX->setText(QString("%1").arg(Dpos[0]));
ui->DposY->setText(QString("%1").arg(Dpos[1]));
ui->DposZ->setText(QString("%1").arg(Dpos[2]));
ui->DposU->setText(QString("%1").arg(Dpos[3]));
//obtain axis speed information
ZAux_Direct_GetAllAxisPara(g_handle,"MSPEED",AxisNum,Mspeed);
ui->MspeedX->setText(QString("%1").arg(Mspeed[0]));
ui->MspeedY->setText(QString("%1").arg(Mspeed[1]));
ui->MspeedZ->setText(QString("%1").arg(Mspeed[2]));
ui->MspeedU->setText(QString("%1").arg(Mspeed[3]));
//obtain motion situation of each axis
floatidle[4]={0};
ZAux_Direct_GetAllAxisPara(g_handle,"idle",AxisNum,idle);
idle[0]=idle[0]+idle[1]+idle[2]+idle[3];
if(idle[0]>(-4))
{
MotionStatus=1;//while moving
}
else
{
MotionStatus=0;
}
}
}
-- Introduction: Basic -- Move_Pt
-- Move_Pt interface encapsulation
A.Encapsulate interface through "ZAux_DirectCommand()"
/*************************************************************
Description: unit time distance
Input: card link handle
motion axes, axis list
motion time, ticks unit, 1ticks≈1ms
motion distance, units unit
Output: No
Return: error code
*************************************************************/
int32 MyApi::ZAux_Direct_MovePt(ZMC_HANDLE handle, int iAxisNum, int *piAxisList, int iTime, float *pfDisList)
{
char cmdbuff[2048],tempbuff[2048];
char cmdbuffAck[2048];
//judge input parameters
if((0 > iAxisNum || iAxisNum > MAX_AXIS_AUX)) return ERR_AUX_PARAERR;
if(NULL == piAxisList) return ERR_AUX_PARAERR;
if(iTime<=0) return ERR_AUX_PARAERR;
if(NULL == pfDisList) return ERR_AUX_PARAERR;
//generate the command to select which axis to move......for example, Basic command, (0,1,2,3) means axis 0, axis 1, axis 2 and axis 3 are selected
//encapsulate Basic axis-selection command through character string splicing command.
BASE(piAxisList[0],piAxisList[1],.....piAxisList[i])
strcpy(cmdbuff, "BASE(");
for(int i = 0; i< iAxisNum-1; i++)
{
sprintf(tempbuff, "%d,",piAxisList[i]);
strcat(cmdbuff, tempbuff);
}
sprintf(tempbuff, "%d)",piAxisList[iAxisNum-1]);
strcat(cmdbuff, tempbuff);
//change the line, keep encapsulating Basic command
strcat(cmdbuff, "\n");
//generate unit time motion distance command,......Basic command is achieved through Move_PT(ticks, dis1,dis2…)
sprintf(tempbuff, "Move_PT(%d,",iTime);
strcat(cmdbuff, tempbuff);
//encapsulate each axis' motion distance
for(int i = 0; i< iAxisNum-1; i++)
{
sprintf(tempbuff, "%f,",pfDisList[i]);
strcat(cmdbuff, tempbuff);
}
sprintf(tempbuff, "%f)",pfDisList[iAxisNum-1]);
strcat(cmdbuff, tempbuff);
//call the command to execute the function
//printf("%s",cmdbuff);
return ZAux_DirectCommand(handle, cmdbuff, cmdbuffAck, 2048);
}
B.Qt routine calls the interface encapsulated just now. -- MyApi::ZAux_Direct_MovePt()
//open Move_Pt motion
voidWidget::on_PtStartButton_clicked()
{
if(0== MotionStatus)
{
intbuffNum=0;
//obtain axis 0 remaining buffers, command only can be sent when the number of axes in buffer is enough.
ZAux_Direct_GetRemain_LineBuffer(g_handle,0,&buffNum);
if(buffNum>3)
{
intAxisList[4]={0,1,2,3};
floatDisList[3][5];
for(inti=0;i<3;i++)
{
for(intj=0;j<5;j++)
{
DisList[i][j]=LineData[i][j]->text().toFloat();
}
//call the function interface encapsulated by onw to move MOVE_PT.
myapi->ZAux_Direct_MovePt(g_handle,AxisNum,AxisList,(int)(DisList[i][0]),&DisList[i][1]);
}
}
else
{
QMessageBox::warning(this,"warning","insufficient axis in buffer");
}
}
else
{
QMessageBox::warning(this,"warning","system is operting......");
}
}
C. Oscilloscope capture
(6)One API is encapsulated through sending several move_ptabs commands to process.
A.Send encapsulations of several move_ptabs commands.
/*************************************************************
Description: send multiple unit time distance instructions in one time
Input: card link handle
the number of motion axes, axis list
motion time, the unit is ticks, 1ticks≈1ms
motion distance, the unit is units
Output: No
Return: error codes
*************************************************************/
int32 MyApi::ZAux_Direct_MovePtAbsS(ZMC_HANDLE handle, int iAxisNum, int *piAxisList, int ApiNum,int *iTime, float *pfDisList)
{
char cmdbuff[2048*128],tempbuff[2048];
char cmdbuffAck[2048];
//input parameters to judge
if((0 > iAxisNum || iAxisNum > MAX_AXIS_AUX)) return ERR_AUX_PARAERR;//the number of axes is incorrect
if(NULL == piAxisList) return ERR_AUX_PARAERR;//axis list is blank
if(iTime== 0) return ERR_AUX_PARAERR;//time list is blank
if(NULL == pfDisList) return ERR_AUX_PARAERR;//motion distance list is blank
if((ApiNum<0)||(ApiNum>50)) return ERR_AUX_PARAERR;//Api numbers is incorrect
//generate the command to select which axes to be moved......for example, Basic command, BASE(0,1,2,3) means axis 0, axis 1, axis 2 and axis 3 are selected
//encapsulate Basic axis selection instruction through character string splicing command
BASE(piAxisList[0],piAxisList[1],.....piAxisList[i])
strcpy(cmdbuff, "BASE(");
for(int i = 0; i< iAxisNum-1; i++)
{
sprintf(tempbuff, "%d,",piAxisList[i]);
strcat(cmdbuff, tempbuff);
}
sprintf(tempbuff, "%d)",piAxisList[iAxisNum-1]);
strcat(cmdbuff, tempbuff);
//keep encapsulating Basic command in new line
strcat(cmdbuff, "\n");
for(int j=0;j<ApiNum;j++)
{
if(iTime[j]>0)
{
//generate unit time motion distance command, ......Basic command is achieved through Move_PT(ticks, dis1,dis2…)
sprintf(tempbuff, "Move_PtAbs(%d,",iTime[j]);
strcat(cmdbuff, tempbuff);
//encapsulate motion distance of each axis
for(int i = 0; i< iAxisNum-1; i++)
{
sprintf(tempbuff, "%.5f,",pfDisList[i+j*iAxisNum]);
strcat(cmdbuff, tempbuff);
}
sprintf(tempbuff, "%.5f)",pfDisList[iAxisNum-1+j*iAxisNum]);
strcat(cmdbuff, tempbuff);
strcat(cmdbuff, "\n");
}
}
//call the command to execute the function
return ZAux_DirectCommand(handle, cmdbuff, cmdbuffAck, 2048);
}
B.Qt routine calls the interface encapsulated just now -- MyApi::ZAux_Direct_MovePtAbsS()
//obtain axis motion remaining buffers
ZAux_Direct_GetRemain_LineBuffer(g_handle,0,&buffNum);
if((buffNum>ApiNum*2)&&(SendNum*1<(4*ui->HorizoScale->text().toFloat())))
{
Num = (float) 4*ui->HorizoScale->text().toFloat();
switch (RunType) {
//((-sin(PI*2*i/T)/(PI*2))+i/T)*500
case 0:
for(int i=0;i<ApiNum;i++)
{
DisList[i]=(float) (((-sin(M_PI*2*SendNum/(Num-1))/(M_PI*2))+SendNum/ (Num-1))*500);
SendNum=SendNum+1; }
break;
default:
break;
}
if(SendNum<ApiNum+10)
{
//set current point
ui->Canvas->StartPoint.setX(0);
ui->Canvas->StartPoint.setY(-DisList[0]*EquivalentY);
ui->Canvas->StopPoint.setX(0);
ui->Canvas->StopPoint.setY(-DisList[0]*EquivalentY);
TimerWaveform->start(10);
QString Str;
char buff[1204];
//send the online command to open oscilloscope
Str=QString("%1%2%3%4%5").arg("SCOPE(ON,1,").arg(0).arg(",").arg(3500).arg(",DPOS(0))");
ZAux_Execute(g_handle,Str.toLatin1().data(),buff,1024);
ZAux_Trigger(g_handle);
QThread::msleep(1);
}
myapi->ZAux_Direct_MovePtAbsS(g_handle,1, &AxisList,ApiNum, TimeList, DisList);
}
C.Qt collects position data of axis motion, and generate position wavaform.
oidDraw::paintEvent(QPaintEvent*event)
{
if(DrawFlag!=0)
{
//example, one painter draws, this means equipment to draw
QPainterPainter(WaveformFigure);
Painter.translate(0,(int)(ImgH/2));
//set the brush
QPenPen(QColor(255,0,0));
Pen.setWidth(4);
Pen.setStyle(Qt::SolidLine);
Painter.setPen(Pen);
//send the command about oscilloscope to be triggered to capture data
if((CurTriggerNum>=SingTriggerNum))
{
qDebug()<<"trigger the oscilloscope"<<SingTriggerNum;
QStringStr;
charbuff[1204];
Str=QString("%1%2%3%4%5").arg("SCOPE(ON,1,").arg(StartTableId).arg(",").arg(StartTableId+SingTriggerNum).arg(",DPOS(0))");
ZAux_Execute(g_handle,Str.toLatin1().data(),buff,1024);
ZAux_Trigger(g_handle);
}
//return the number of points of current SCOPE captured data
intoldTriggerNum=CurTriggerNum;
ZAux_Direct_GetUserVar(g_handle,"SCOPE_POS",&CurTriggerNum);
//obtain current SCOPE captured data
ZAux_Direct_GetTable(g_handle,StartTableId+oldTriggerNum,CurTriggerNum-oldTriggerNum,Data);
//Qlist data processing
QList<float>::iteratoriet;
for(inti=0;i<CurTriggerNum-oldTriggerNum;i++,PointNum++)
{
iet= EquivalentDataY.begin()+PointNum;
//set starting point
if(0==i)
{
StartPoint.setY(-EquivalentY*(*(iet-1)));
StartPoint.setX((PointNum-1)*EquivalentX);
}
else
{
StartPoint=StopPoint;
}
//obtain the position of new point
if(iet<EquivalentDataY.end())
{
*iet=Data[i];
}
else
{
EquivalentDataY.append(Data[i]);
}
StopPoint.setY(-EquivalentY*(Data[i]));
StopPoint.setX((PointNum)*EquivalentX);
//draw the line
if((StartPoint!= StopPoint)&& (PointNum>0))
{
Painter.drawLine(StartPoint,StopPoint);
}
}
}
}
D.Capture waveform to check effects.
a.Y=((-sin(PI*2*i/T)/(PI*2))+i/T)*500 speed and position curve
b.Qt captures Y=((-sin(PI*2*i/T)/(PI*2))+i/T)*position curve of 500
That's all, thank you for your reading -- EtherCAT motion control card -- Custom Curve.
This article is edited by ZMOTION, here, share with you, let's learn together.
ZMOTION: DO THE BEST TO USE MOTION CONTROL.
Note: Copyright belongs to ZMotion Technology, if there is reproduction, please indicate article source. Thank you.
Top comments (0)