Delta Parallel Robotic Arm by C# | Vision Synchronous Sorting

ZMOTION CONTROLLER - Feb 18 - - Dev Community

Today, Zmotion shares "how to do synchronous sorting in assemble line through C# for Delta robotic arm".

Also, we combine our "vision motion controller" with C# to achieve that.
"Vision Motion Controller" -- "VPLCXXX" -- "VPLC711"

PART ONE:
Delta Parallel Robotic Arm by C# | How to Do Kinematic and Manual

PART TWO:
Delta Parallel Robotic Arm by C# | How to Do Camera Calibration & Shape Matching

01

How to Add Motion and Vision Libraries for Delta Robotic Arm by C#

Here, use VS2010.

Step 1: Build One Project
--click "File" - "New" - "Project"--
--select "Visual C#", .Net Framework 4 and Windows program--

Step 2: Obtain Zmotion C# Library
--method 1: find corresponding library in Zmotion website "download", then download by yourself--
--method 2: contact us, send the corresponding library to you--

Step 3: Develop by Zmotion C# Library File
Note: the downloaded library is "motion control library", if you need vision function, please obtain vision library from us.

--copy "Zmcaux.cs" (motion library) and "Zvision.cs" (vision library) files into new built project--

Image description

--put zauxdll.dll, zmotion.dll, and zvision.dll files into folder of bin\debug--

Image description

--open the file, and click "show all files" in the "solution resource manager", and right click "Zmcaux.cs" and "Zvision.cs" files, then click "include in project".

Step 4: Edit
--double click "Form 1" of "Form 1.cs"--
--edit "using cszmcaux" and "using ZVision" at the beginning--
--state controller handle "g_handle"--

Image description

0 2

Main PC Commands for Delta

In "PC Function Library Programming Manual”, you can check all encapsulated commands. It can be downloaded here or contact us directly.

Here, it will show Delta Synchronous Sorting related commands.

(1) Connect to Controller -- ZAux_OpenEth

Image description

(2) Synchronize -- ZAux_Direct_MoveSync

Image description

03

How to Use Basic Command to Check MoveSync

(1) Edit Basic to Test MoveSync Usage

'**************************************************************************************
'if there are one pair of “through-beam photoelectric sensor” fixed at two sides of assemble line to real-time detect the product position on the assemble line.
'how to fill in MOVESYNC command parameters: 
'syncposition: the belt axis position when the object arrived at induction position, it needs to record product position through encoder latching. 
'pos1: the position from following axis 1 origin to photoelectric sensor induction position is fixed for each product.
'**************************************************************************************
GLOBAL CONST BeltAxis=4      'belt axis is axis 4
GLOBAL CONST FollowAxis1=0  'following axis 1 is axis 0
GLOBAL CONST InducPos1=30  'the position from following axis 1 origin to photoelectric sensor induction point
GLOBAL CONST StandbyPos1=50'following axis 1’ standby position
GLOBAL CONST EmptyPos1=400  'following axis 1’s unloading position
'stop all axes
RAPIDSTOP(2)
WAIT IDLE     
'initialize axis parameters of corresponding axes
BASE(FollowAxis1, BeltAxis)
ATYPE = 1,1
UNITS = 1000,1000
SPEED = 50,100
DPOS = 0,0
'trigger SCOPE to capture data waveform
TRIGGER
DELAY(1000)
'following axis moves to standby at first
BASE(FollowAxis1)
MOVEABS(StandbyPos1)
'belt axis starts to move
VMOVE(1)  AXIS(BeltAxis)
'if there is one product detected when belt moved at 200
BASE(FollowAxis1)
Wait UNTIL MPOS(BeltAxis)> 200
MOVESYNC(0, 2000, 200, BeltAxis, InducPos1) 'when the command (following axis accelerates to synchronize) executed, it will synchronize with the product
MOVE_OP(0, ON)                           'synchronized, ON vacuum suction 
MOVESYNC(0, 1000, 200, BeltAxis, InducPos1) 'keep synchronizing 1s
MOVESYNC(-1, 0, 0, -1, EmptyPos1)          'move to unloading position
MOVE_OP(0, OFF)                       'arrive unloading position, off vacuum suction
Enter fullscreen mode Exit fullscreen mode

(2) Check Data Waveform

Obtain corresponding waveform through RTSys (ZDevelop) -- SCOPE, then analyze synchronization process.

Image description

Image description

According above scope data we can know:

  • a. when product is detected, the belt position is 200, following axis position is 50.
  • b. following axis follows with the product, and keeps the same speed, when it synchronized with belt axis, belt position is 400, following axis 1 position is 230.
  • c. it can be know the product moves forward 200 (400-200) while synchronizing from "a" and "b".
  • d. the data from following axis 1 origin to photoelectric sensor's sense point position "InducPos1" is 30, so when axis 1 is parallel with belt, belt makes product move forward 200, then now the real distance becomes 230 (200+30).
  • e. therefore, the result of "d" is consistent with following axis 1 real position after synchronized, that is, assemble synchronous command is OK.

04

One Routine: How C# Do Vision Synchronous Sorting in Assemble Line

(1) Belt Synchronized Command Key Parameters

ZAux_Direct_MoveSync(ZMC_HANDLE handle,float imode,int synctime, float syncposition, int syncaxis, int imaxaxises, int piAxislist, float *pfDisancelist)
*
--imode--**
Select synchronized mode.
imode=0+angle: synchronous mode, if belt and axis X are parallel, fill in 0.
imode =-1: end mode, move to the specified absolute position, it is usually used at the unloading position after synchronized and captured the material.

--synctime--
Synchronization time, in ms unit. The movement is completed within the specified time. When completed, axis speed and object speed (on the belt axis) keep same. 0 indicates that the synchronization time is estimated according to motion axis' speed and acceleration.

--syncposition--
When vision or sensor sensed product on the belt, now belt position is MPOS (measurement position).

--pfDistancelist--
If locates the product by vision, this parameter is "world coordinate" of the product when vision recoginized the product.

If it uses photoelectric sensor to detect the product, this parameter is fixed, when the sensor feels the product, product current position absolute coordinate. Now, it can move slave axis manually to positioning the product to get the position.

(2) Vision Synchronous Sorting Steps

Image description

A. Codes Details of Vision Matching & Positioning

/************************************************************************************
'Task No.: /
'Function of this function: vision positioning
'Input: /
'Output: / 
'returned value: sub thread – do vision positioning 
**************************************************************************************/
public void RunSubTaskVisua()
{
    int TempArrid = 0;
    float TempVar = 0;
    WriteLog("Vision Function ON");
    while (SysRunFlag > 0)
    {
        //when pause button is not pressed
        while (SysRunFlag == 1) 
        {
            //capture the image
            VisuaOper.CameAcquisition();
            //do template matching
            RTDisplay.Image = VisuaOper.ShapeFind();
            if (MainWindows.BeltMpos != 0)//if gets the belt encoder position normally when capturing the image
            {
                //operate MoveSyncBuff data, please lock at first
                while (true)
                {
                    if (MainWindows.SetMoveSyncFlag == 0)
                    {
                        MainWindows.SetMoveSyncFlag = 1;
                        break;
                    }
                }
                //find array starting subscript that can save data
                int ArrId = 0;
                for (int i = 0; i < 50; i++)
                {
                    if (MainWindows.MoveSyncBuff[i, 0] == 0)
                    {
                        ArrId = i;
                        break;
                    }
                }
                //start to save the data, 10 results can be matched at most
                TempArrid = ArrId;
                for (int i = 0; i < 10; i++)
                {
                    //if the score is OK
                    if (MainWindows.VisionRst[i, 0] >= MainWindows.VisionScore)
                    {
                        int j;
                        //if there is repeated target, delete repeated one
                        for (j = 0; j < TempArrid; j++)
                        {
                            TempVar = MainWindows.VisionRst[i, 1] - MainWindows.BeltMpos - MainWindows.MoveSyncBuff[j, 1] + MainWindows.MoveSyncBuff[j, 4];
                            if (((TempVar) <= 10) && (TempVar >= -10))
                            {
                                j = -10;
                                break;
                            }
                        }
                        if (j >= 0)
                        {
                            MainWindows.MoveSyncBuff[ArrId, 0] = 1;
                            MainWindows.MoveSyncBuff[ArrId, 1] = MainWindows.VisionRst[i, 1];   //save matched X coordinate
                            MainWindows.MoveSyncBuff[ArrId, 2] = MainWindows.VisionRst[i, 2];   //save matched Y coordinate
                            MainWindows.MoveSyncBuff[ArrId, 3] = MainWindows.VisionRst[i, 3];   //save matched angle offset
                            MainWindows.MoveSyncBuff[ArrId, 4] = MainWindows.BeltMpos;       //save belt position when product is matched
                            ArrId = ArrId + 1;
                            IdentiNum.Text = (Convert.ToInt32(IdentiNum.Text) + 1).ToString();
                            WriteLog("Vision Target:" + "(" + MainWindows.VisionRst[i, 1].ToString("0,0") + "," + MainWindows.VisionRst[i, 2].ToString("0,0") + ")");
                        }
                    }
                    //clear scores
                    MainWindows.VisionRst[i, 0] = 0;
                }
                //unlock
                MainWindows.SetMoveSyncFlag = 0;
            }
        }
        Thread.Sleep(100);
    }
}
Enter fullscreen mode Exit fullscreen mode

B. Codes Details of Vision Sorting in Assemble Line

/************************************************************************************
'Task No.: /
'Function of this function: assemble line synchronous sorting
'Input: /
'Output: / 
'returned value: /          
**************************************************************************************/
public void RunSubTaskMotion()
{
    float[] MoveSyncTemp = new float[5];
    float TempMpos = 0;
    while (SysRunFlag > 0)
    {
        while (SysRunFlag == 1)
        {
            if (MainWindows.MoveSyncBuff[0, 0] == 1)
            {
                MainWindows.ZauxErr = zmcaux.ZAux_Direct_GetMpos(MainWindows.g_Handle, MainWindows.ConveyorAxisId, ref TempMpos);
                //if gets the encoder position correctly
                if (0 == MainWindows.ZauxErr) 
                {
                    //how far the encoder moves forward
                    TempMpos = TempMpos - MainWindows.MoveSyncBuff[0, 4];


                    //check whether it is at synchronization starting area
                    if (((MainWindows.MoveSyncBuff[0, 1] + TempMpos) >= MainWindows.SyncReX[0]) && ((MainWindows.MoveSyncBuff[0, 1] + TempMpos) <= MainWindows.SyncReX[1]))
                    {
                        WriteLog("Start to Synchronize to Capture");
                        //get one group of data
                        MoveSyncTemp[0] = MainWindows.MoveSyncBuff[0, 1] ;    //X
                        MoveSyncTemp[1] = MainWindows.MoveSyncBuff[0, 2] ;    //Y  
                        MoveSyncTemp[2] = MainWindows.GetBinHigt;            //capture height
                        MoveSyncTemp[3] = MainWindows.MoveSyncBuff[0, 3];    //Aanle
                        MoveSyncTemp[4] = MainWindows.MoveSyncBuff[0, 4];    //Mpos
                       //send synchronous motion command
                       //0, reset OUT
                        zmcaux.ZAux_Direct_MoveOp(MainWindows.g_Handle, MainWindows.gVAxisList[0], MainWindows.VacSucIo, 0);
                        //1, synchronize with transfer belt at first
                        zmcaux.ZAux_Direct_MoveSync(MainWindows.g_Handle, 0, 0, MainWindows.MoveSyncBuff[0, 4], MainWindows.ConveyorAxisId, 4, MainWindows.gVAxisList, MoveSyncTemp);
                        //2, synchronize a certain time, (there is lag of joint axis) 50ms
                        zmcaux.ZAux_Direct_MoveSync(MainWindows.g_Handle, 0, 50, MainWindows.MoveSyncBuff[0, 4], MainWindows.ConveyorAxisId, 4, MainWindows.gVAxisList, MoveSyncTemp);
                        //3, open vacuum nozzle
                        zmcaux.ZAux_Direct_MoveOp(MainWindows.g_Handle, MainWindows.gVAxisList[0], MainWindows.VacSucIo, 1);
                        //4, synchronize a certain time, 1500ms
                        zmcaux.ZAux_Direct_MoveSync(MainWindows.g_Handle, 0, 700, MainWindows.MoveSyncBuff[0, 4], MainWindows.ConveyorAxisId, 4, MainWindows.gVAxisList, MoveSyncTemp);
                        //5, in synchronization, move axis Z up to safe height, select axis unloading angle
                        MoveSyncTemp[2] = MainWindows.StandPos[2];             //capture height
                        zmcaux.ZAux_Direct_MoveSync(MainWindows.g_Handle, 0, 100, MainWindows.MoveSyncBuff[0, 4], MainWindows.ConveyorAxisId, 4, MainWindows.gVAxisList, MoveSyncTemp);
                        //4, release “synchronize to unloading point”
                        MoveSyncTemp[0] = MainWindows.EmptPos[0];    //X
                        MoveSyncTemp[1] = MainWindows.EmptPos[1];    //Y  
                        MoveSyncTemp[2] = MainWindows.EmptPos[2];    //unloading height
                        MoveSyncTemp[3] = MainWindows.EmptPos[3];    //Aanle
                        zmcaux.ZAux_Direct_MoveSync(MainWindows.g_Handle, -1, 0, 0, -1, 4, MainWindows.gVAxisList, MoveSyncTemp);
                        //5, close vacuum nozzle to place material, delay 100ms
                        zmcaux.ZAux_Direct_MoveOp(MainWindows.g_Handle, MainWindows.gVAxisList[0], MainWindows.VacSucIo, 0);
                        zmcaux.ZAux_Direct_MoveDelay(MainWindows.g_Handle, MainWindows.gVAxisList[0], 100);
                        //6, move to safe height
                        MoveSyncTemp[0] = MainWindows.EmptPos[0];    //X
                        MoveSyncTemp[1] = MainWindows.EmptPos[1];    //Y  
                        MoveSyncTemp[2] = MainWindows.StandPos[2];    //unloading height
                        MoveSyncTemp[3] = MainWindows.EmptPos[3];    //Aanle
                        zmcaux.ZAux_Direct_MoveAbs(MainWindows.g_Handle, 4, MainWindows.gVAxisList, MoveSyncTemp);


                        //operate MoveSyncBuff data, please lock at first
                        while (true)
                        {
                            if (MainWindows.SetMoveSyncFlag == 0)
                            {
                                MainWindows.SetMoveSyncFlag = 1;
                                break;
                            }
                        }
                        //in vision matching buffer, data are covered
                        for (int k = 0; k < 49; k++)
                        {
                            MainWindows.MoveSyncBuff[k, 0] = MainWindows.MoveSyncBuff[k + 1, 0];
                            MainWindows.MoveSyncBuff[k, 1] = MainWindows.MoveSyncBuff[k + 1, 1];
                            MainWindows.MoveSyncBuff[k, 2] = MainWindows.MoveSyncBuff[k + 1, 2];
                            MainWindows.MoveSyncBuff[k, 3] = MainWindows.MoveSyncBuff[k + 1, 3];
                            MainWindows.MoveSyncBuff[k, 4] = MainWindows.MoveSyncBuff[k + 1, 4];
                        }
                        //unlock
                        MainWindows.SetMoveSyncFlag = 0;
                        //until OUT opens
                        int TimeOut = 10000;
                        TimeOut = 100000;
                        //until axis stops
                        int AxisIdle = 0;                 //axis stopped state
                        while (TimeOut > 0)
                        {
                            zmcaux.ZAux_Direct_GetIfIdle(MainWindows.g_Handle, MainWindows.gVAxisList[0], ref AxisIdle);
                            if (AxisIdle == (-1))
                            {
                                break;
                            }
                            Thread.Sleep(10);
                            TimeOut = TimeOut - 10;
                        }
                        if (TimeOut <= 0)
                        {
                            //if timeout, report an error, program stops
                            WriteLog("Timeout Waiting for Axis to Stop ");
                            //Thread.Sleep(100);
                            SysRunFlag = 0;
                            break;
                        }
                        SortNum.Text = (Convert.ToInt32(SortNum.Text) + 1).ToString();
                        WriteLog("Unloading Succeeded ");
                        //exit while, cycle once
                        continue;
                    }
                    else if ((MainWindows.MoveSyncBuff[0, 1] + TempMpos) > MainWindows.SyncReX[1])
                    {
                        //operate MoveSyncBuff data, please lock at first
                        while (true)
                        {
                            if (MainWindows.SetMoveSyncFlag == 0)
                            {
                                MainWindows.SetMoveSyncFlag = 1;
                                break;
                            }
                        }
                        //in vision matching buffer, data are covered
                        for (int k = 0; k < 49; k++)
                        {
                            MainWindows.MoveSyncBuff[k, 0] = MainWindows.MoveSyncBuff[k + 1, 0];
                            MainWindows.MoveSyncBuff[k, 1] = MainWindows.MoveSyncBuff[k + 1, 1];
                            MainWindows.MoveSyncBuff[k, 2] = MainWindows.MoveSyncBuff[k + 1, 2];
                            MainWindows.MoveSyncBuff[k, 3] = MainWindows.MoveSyncBuff[k + 1, 3];
                            MainWindows.MoveSyncBuff[k, 4] = MainWindows.MoveSyncBuff[k + 1, 4];
                        }
                        //unlock
                        MainWindows.SetMoveSyncFlag = 0;
                        //if there is no data in the vision matching buffer area
                        if (MainWindows.MoveSyncBuff[0, 0] == 0)
                        {
                            //Delta moves to standby position
                            zmcaux.ZAux_Direct_MoveAbs(MainWindows.g_Handle, 4, MainWindows.gVAxisList, MainWindows.StandPos);
                            WriteLog("To Standby");
                        }
                    }
                }
            }
            else
            {
                //Delta moves to standby position
                zmcaux.ZAux_Direct_MoveAbs(MainWindows.g_Handle, 4, MainWindows.gVAxisList, MainWindows.StandPos);
            }
            Thread.Sleep(50);
        }
        //stop transfer belt
        zmcaux.ZAux_Direct_Single_Cancel(MainWindows.g_Handle, MainWindows.ConveyorAxisId, 2);
    }
}
Enter fullscreen mode Exit fullscreen mode
. . . . . . . .