Good Morning and welcome to my vision Tutorial. In this tutorial I will explain how to use Tc3_Vision to read the barcode and encode the data, Display by TE1800.There are 2 new concepts that are named “Watchdog” and “Contours”.Do not worry, and I will try my best to explain it easily.Let’s Start!
Watchdogs!!!
In this tutorial , there is a new concept called “Watchdogs”. As we know, watchdog is the function that monitors overtime or something like this.This feature is very important in the Image process – Because function calls can take a long time and may affect the main control task of your control system.
Here are some case may affect the processing time:
- changing lighting conditions
- unexpected objects in the image
- Special Functions like F_VN_FindContours() – The images that tokenize in the normal lighting, only 10 contours are found but 100 contours may be found in the un normal conditions.
These abnormal operations may trigger your system to cycle overruns and be undefined behavior.
So- Watch Dogs is very useful here and monitoring the execution time of the Functions can be stopped if the execution time is too long.
Activate
Go to your Plc project> SYSTEM>Task>your Task.
There is a checkbox called “Watchdog stack”.
Please check it.
Contours
OK, the next concept that I would like to explain is Contours.
Contours, also called an outline, is the demarcation of an object from its surroundings.
In the picture below, you may see a small red frame that is put in the barcode label.
That is the contours.
The Contours describes these item of the object:
- shape
- size
- location of that object
Fields of contours
we can find more than 1 contours in One Images and found by F_VN_FindContours().In technically all data will save as a 2D container Array.
Contour[0].point.. something.
Function block
This time I will explain the New Function/Function Blocks that are used in this tutorial.
FB_VN_WriteImage
This FB writes an image to the hard drive.
VAR_INPUT
ipImage | Reference To ITcVnImage | The return image that is loaded. |
sFilePath | STRING | The Full path of your Image file. |
bWrite | BOOL | use a rising edge to write the image as file |
nTimeout | TIME VISION_ADS_TIME OUT | The time before the function is canceled. |
VAR_OUTPUT
bBusy | BOOL | True=Function Block is running.. |
bError | BOOL | True=Error has occurred. |
nErrorId | UDINT | Error information |
F_VN_StartRelWatchdog
Starts a cooperative watchdog given a stop time relative to the current time.
VAR_INPUT
tStop | DINT | Stop time in us |
hrPrev | HRESULT | The last execute result |
Return Value
F_VN_StartRelWatchdog | HRESULT | The execute result |
F_VN_StopWatchdog
Stop a watchdog and provide runtime information.
VAR_INPUT
hrStartWatchdog | HRESULT | The execute result of start watchdog function |
VAR_OUTPUT
nFunctionsMonitored | ULINT | Returns the number of functions monitored. |
bEnFractionProcerrorssed | UDINT | Returns the fraction processed accumulated over the monitored functions in percen |
tRest | DINT | Returns the remaining computation time in us (may be negative) |
Return Value
F_VN_StopWatchdog | HRESULT | The execute result |
F_VN_PutTextExp
Write text into an image.
VAR_INPUT
sText | STRING | Text ipDestImage |
nX | UDINT | x coordinate (bottom left) |
nY | UDINT | y coordinate (bottom left) |
eFontType | ETcVnFontType | |
fFontScale | LREAL | Scaling factor |
aColor | Reference To TcVnVector4_LREAL | Text color |
nThickness | DINT | Line thickness |
eLineType | ETcVnLineType | |
bBottomLeftOrigin | BOOL | Sets the image origin to the bottom left corner, if true |
hrPrev | HRESULT | indicating the result of previous operation |
Return Value
F_VN_PutTextExp | HRESULT | The execute result |
ETcVnFontType
Offers font types.
Further information
ETcVnLineType
Offers line types
F_VN_DrawContours
Here is a function that allows us to draw a single point set or multiple point sets that are interpreted as contours.
in an Image.
- The Contour is defined based on the Container that you passed into the function.
- The contours that are saved into your Container are based on your find contours functions.
- In this Tutorial, the Contours are found by function F_VN_ReadBarcodeExp()
VAR_INPUT
ipContours | ITcVnContainer | Single contour (ContainerType_Vector_TcVnPoint2_DINT) or multiple contours (ContainerType_Vector_Vector_TcVnPoint2_DINT) |
nContourIndex | DINT | Index of a specific contour to be drawn (if negative, all contours within the container are drawn) |
ipDestImage | ITcVnImage | Destination image |
eFontType | ETcVnFontType | |
aColor | Reference To TcVnVector4_LREAL | Text color |
nThickness | DINT | Line thickness (if negative, the contours are filled) |
hrPrev | HRESULT | indicating the result of previous operation |
Return Value
F_VN_DrawContours | HRESULT | The execute result |
F_VN_ReadBarcodeExp
This Function is the Expert version of F_VN_ReadBarCode. you can Detect and interpret a 1d barcode within the provided image and return the code position and for setting the search direction.
VAR_INPUT
ipSrcImage | ITcVnImage | Source image Type:USINT elements, 1 channel or 3 channel if 3 channel input is expected to be RGB and internally converted to Gray. |
ipDecodedData | Reference ToITcVnContainer | Returns the decoded code Type:ContainerType_Vector_String_SINT |
ipContours | ITcVnContainer | Returns the code positions as contours Type:ContainerType_Vector_Vector_TcVnPoint2_DINT |
eBarcodeType | UDINT | Types of barcode to search for (ETcVnBarcodeType)All enum values of ETcVnBarcodeType are supoorted. |
nCodeNumber | DINT | How many barcodes can be searched in images.Only1 is supported. |
eSearchDirection | ETcVnBarcodeSearchDirection | Barcode search direction. |
hrPrev | HRESULT | indicating the result of previous operation |
Return Value
F_VN_ReadBarcodeExp | HRESULT | The execute result |
ETcVnBarcodeSearchDirection
TCVN_BSD_ANY | first searches in horizontal direction, then in vertical direction. |
TCVN_BSD_HORIZONTAL | only searches in horizontal direction |
TCVN_BSD_VERTICAL | only searches in vertical direction |
ETcVnBarcodeType
F_VN_ExportSubContainer_String
we can use this function to export the elements into a container as a String.
Only 2D Containers with type ContainerType_Vector_String_SINT are available in this function.
VAR_INPUT
ipContours | ITcVnContainer | Container of type ContainerType_Vector_String_SINT |
nIndex | ULINT | the index of requested element |
sText | STRING | Text output from Container |
nMaxLength | ULINT | |
hrPrev | HRESULT | indicating the result of previous operation |
Return Value
F_VN_ExportSubContainer_String | HRESULT | The execute result |
Implemention
Same as last post, I will not explain too much on the RoboDK Side.
Please Download the source project in the Final part of this tutorial.
Configuration
Also Same as the last tutorial, we will use the RoboDK 2D Camera Simulation interface.
and Connect with Beckhoff TwinCAT by using TF6100 OPCUA.
Flow
Here is the flow for RoboDK and twinCAT handshake.
Barcode Reading Flow
This is the Flow of how my sample program works.
It just loads the images>Read the barcode>export the string>draw the text and contours>export the image again.
OPCUA Connections
Please reference the following link to configurare an OPC UA Server.
Beckhoff#Using TwinCAT TF6100 to startup OPCUA Server | (soup01.com)
Script to Create BarCode
Here is a script to generate with random numbers.
from robolink import * # RoboDK API from robodk import * # Robot toolbox from random import choice import barcode def CreateRandomBarCode(i): sequence = [i for i in range(9)] BarCodeList=[] for _ in range(i): st=” for _ in range(13): sel=choice(sequence) st1=str(sel) st+=st1 #print(st) #print(type(st)) BarCodeList.append(st) return BarCodeList BarCodeList=CreateRandomBarCode(5) print(BarCodeList) BASE_PATH=r’C:\images\barcode_’ for i in range(1,5): print(BASE_PATH+str(i)+’.png’) data = mbox(“Enter your bar code (EAN 13 digits)”, entry=’5701234567899′) if data is None or data is False or type(data) is not str: # User cancelled quit() if not data.isdecimal() or len(data) != 13: # Invalid input quit() #img = barcode.EAN13(data, writer=barcode.writer.ImageWriter()) name = “EAN13_” + data.replace(‘.’, ”).replace(‘:’, ”).replace(‘/’, ”) filename = None if filename is None or filename == ”: import_install(“tempdir”) import tempfile tempdir = tempfile.gettempdir() filename = r’C:\images\barcode1.png’ for i in range(len(BarCodeList)): img = barcode.EAN13(BarCodeList[i], writer=barcode.writer.ImageWriter()) filename=BASE_PATH+str(i)+’.png’ img.save(filename) #img.save(filename) filename += ‘.png’ # barcode .save adds the .png import os.path if not os.path.exists(filename): quit(-1) |
Program
Here is the code that reads the image from the file system and then detects the Barcode. Display the information by TE1800.
VAR
VAR //FB FB_Images:FB_VN_ReadImage; WriteImages:FB_VN_WriteImage; R_TRIG1,R_TRIG2,R_TRIG5 :R_TRIG; TON:TON; hr,hr2:HRESULT:=S_OK; index:ULINT; //Pointers ipImage:ITcVnImage; ipDecodedData:ITcVnContainer; stImageInfo_1:TcVnImageInfo; ImageInfo:TcVnImageInfo; //System Devices read:BOOL; readQR:BOOL; binit:BOOL; safetyInit:BOOL; bwrite:BOOL; b3,b4:BOOL; i:INT; //Image Information width:UDINT; height:UDINT; sText:STRING; // Watchdog hrWD : HRESULT; tStop: DINT := 50000; tRest: DINT; sWatchDogTime:STRING; // BarCode readBarCode:BOOL; ipCodeContourList:ITcVnContainer; eBarcodeType : ETcVnBarcodeType := TCVN_BT_EAN13; eBarcodeSearchDirection : ETcVnBarcodeSearchDirection := TCVN_BSD_ANY; imagePath:STRING:=’C:\images\Images_read’; imageMarkedPath:STRING:=’C:\images\Images_read_barcode’; pathOrg,pathMarked:STRING; // Color aColorRed : TcVnVector4_LREAL := [255, 0, 0]; //HMI hmiIndex:INT; hmis:ARRAY[1..4]OF DUT_VisionDisplay; EmptyImageInfo:TcVnImageInfo; END_VAR |
Code
//init IF binit THEN IF ipImage <> 0 THEN ipImage.TcRelease(); ipImage:=0; END_IF binit:=FALSE; END_IF //Safety Init IF safetyInit THEN FW_SafeRelease(ADR(ipImage)); FW_SafeRelease(ADR(ipDecodedData)); FW_SafeRelease(ADR(ipCodeContourList)); safetyInit:=FALSE; read:=FALSE; readQR:=FALSE; hr:=0; hr2:=0; FOR i :=1 TO 4 DO hmis[i].Code:=”; hmis[i].Info:=EmptyImageInfo; hmis[i].watchDog:=”; hmis[i].urlMarked:=”; hmis[i].urlOrg:=”; END_FOR i:=0; END_IF //HandShake with RoboDK R_TRIG1 (CLK:=GVL_OPCUAServer.Node.Command2=1); R_TRIG5( CLK:=b4 OR GVL_OPCUAServer.Node.Command2 =41 ); IF R_TRIG5.Q THEN i:=1; b4:=TRUE; END_IF IF R_TRIG1.Q THEN GVL_OPCUAServer.Node.Command1:=0; END_IF //BarCode Reading Program IF b4 THEN CASE i OF //init the path,Release the pointer //Trigger the Read Command 1..4: pathOrg:=”; pathMarked:=”; pathOrg:=CONCAT(STR1:=imagePath,INT_TO_STRING(i)); pathOrg:=CONCAT(pathOrg,’.png’); hmis[i].urlOrg:=pathOrg; pathMarked:=CONCAT(STR1:=imageMarkedPath,INT_TO_STRING(i)); pathMarked:=CONCAT(pathMarked,’.png’); hmis[i].urlMarked:=pathMarked; i:=1+i*10; FB_Images.bRead:=TRUE; //Read the Image 11,21,31,41: FB_Images( sFilePath:=pathOrg ,ipDestImage:=ipImage ); IF ipImage <> 0 THEN i:=i+1; FB_Images.bRead:=FALSE; FB_Images( sFilePath:=pathOrg ,ipDestImage:=ipImage ); END_IF //Take a 0.1s Delay 12,22,32,42: TON( IN:=TRUE ,PT:=T#0.1S ); IF TON.Q THEN TON( IN:=FALSE ); i:=i+1; END_IF //Get the Image Info Data and Transfer to HMI 13,23,33,43: hmiIndex:=i/10; IF hmiIndex >0 AND hmiIndex <=4 THEN F_VN_GetImageInfo( ipImage ,stImageInfo:=hmis[hmiIndex].Info ,hrPrev:=hr ); i:=i+1; END_IF //Search the BarCode 14,24,34,44: hrWD:=F_VN_StartRelWatchdog(tStop:=tStop,hrPrev:=hrWD); hr:=F_VN_ReadBarcodeExp( ipSrcImage:=ipImage ,ipDecodedData:=ipDecodedData ,ipContours:=ipCodeContourList ,eBarcodeType:=eBarcodeType ,nCodeNumber:=1 ,eSearchDirection:=eBarcodeSearchDirection ,hrPrev:=hr2 ); hrWD := F_VN_StopWatchdog(hrWD, tRest => tRest); IF SUCCEEDED(hr) THEN i:=i+1; END_IF //Image Drawing Operation 15,25,35,45: //Export the String that Read from Image hr := F_VN_ExportSubContainer_String( ipContainer:=ipDecodedData ,nIndex:=0 ,sText:=sText ,nMaxLength:=255 ,hrPrev:=hr2 ); //Write the Code that you read to HMi hmis[hmiIndex].Code:=sText; //Put BarCode Values into Image hr:=F_VN_PutTextExp( sText:=sText ,ipDestImage:=ipImage ,nX:=20 ,nY:=20 ,eFontType:=ETcVnFontType.TCVN_FT_HERSHEY_PLAIN ,fFontScale:=1 ,aColor:=aColorRed ,nThickness:=2 ,eLineType:=TCVN_LT_4_CONNECTED ,bBottomLeftOrigin:=FALSE ,hrPrev:=hr2 ); //Draw Contours to mark out the barcode in the Image hr:=F_VN_DrawContours( ipContours:=ipCodeContourList ,nContourIndex:=0 ,ipDestImage:=ipImage ,aColor:=aColorRed ,nThickness:=1 ,hrPrev:=hr2 ); //Draw the WatchDog Time into Images sWatchDogTime := CONCAT(CONCAT(‘Time: ‘, DINT_TO_STRING(tStop – tRest)), ‘us’); //Put the Text hr:=F_VN_PutTextExp( sText:=sWatchDogTime ,ipDestImage:=ipImage ,nX:=20 ,nY:=50 ,eFontType:=ETcVnFontType.TCVN_FT_HERSHEY_SIMPLEX ,fFontScale:=0.5 ,aColor:=aColorRed ,nThickness:=1 ,eLineType:=TCVN_LT_4_CONNECTED ,bBottomLeftOrigin:=FALSE ,hrPrev:=hr2 ); //Write the Watchdog time value that you read to HMi hmis[hmiIndex].watchDog:=sWatchDogTime; //IF all process is OK, jumpt to next Step by +1 IF SUCCEEDED(hr) THEN i:=i+1; END_IF //Write to image to file system 16,26,36,46: IF ipImage <> 0 THEN WriteImages( ipImage:=ipImage ,sFilePath:=pathMarked ,bWrite:=TRUE ); END_IF //Release the pointer IF (NOT WriteImages.bBusy AND NOT WriteImages.bError) OR WriteImages.bError THEN bWrite:=FALSE; WriteImages( bWrite:=FALSE ,sFilePath:=pathMarked ); FW_SafeRelease(ADR(ipImage)); FW_SafeRelease(ADR(ipDecodedData)); FW_SafeRelease(ADR(ipCodeContourList)); //Check the step again //To Do list:any Clever way? //if Step=46, all image are read and back to 0. CASE i OF 16: i:=2; 26: i:=3; 36: i:=4; 46: i:=0; b4:=FALSE; //A ungly step, finding way to get the status from RoboDK that the Program is finished GVL_OPCUAServer.Node.Command2:=0; END_CASE END_IF; END_CASE END_IF |
Visualization
Here is the screen for me to display the image information.
Sample Code
TwinCAT3/TwinCAT Project_Vision_Part2_ReadBarCode.zip at main · soup01Threes/TwinCAT3 (github.com)