みなさんこんにちは。またTc3_Visionの記事を見てくださってありがとうございます。今回はTc3_visionを使用しBarCodeを読み込んでみます。WatchdogとContoursという新しいコンセプトが出てきますが難しくないので一緒に進みましょう。
Watchdogs
まずWatchdogから説明します。Watchdogは通信のタイムアウトなどでよく出てくる言葉ですが、この機能は画像処理でも大切な役割があります。これは関数は呼び出しましたが、処理時間が長すぎて本来の制御タスクが回さなくなるというリスクがあるからです。
では、どんな理由で処理時間が長くなるのでしょうか。
- カメラの照明が急激に変わった
- 望まないものが画像内で現れてる
特殊な関数F_VN_FindContours()などが使用するとき。例えば正常の照明では10個のObjectが検知できる予定ですが、急激に変わったら本来見えなくなるものが見えてしまっ
て検知できるものが急に100に変わったになるなど。
そのような状況のせいでMain Systemに影響されたくないので、Watchdog機能を使用します。
Activate
Plc project> SYSTEM>Task>実行Task.
Watchdog stackのCheckboxをいれてください。
Contours
次はContoursについて説明します。ContoursはFrameやOutlineの意味でObjectの周囲を明確認識できるように枠です。下図のように赤枠がBarcodeを示していますね。
このContoursは以下のものを示しています;
- Shape
- サイズ
- Objectの位置
Fields of contours
もちろん画像内で1つ以上のContoursが存在するのも普通です。基本的にこのように2D配列式に格納します.
Function block
今回もExample内で使用するFunction/Function Blockのみ説明します。
FB_VN_WriteImage
このFunction blockはTwinCATの画像をFile Systemに保存します。
VAR_INPUT
ipImage | Reference To ITcVnImage | File Systemに保存する画像 |
sFilePath | STRING | 保存先 |
bWrite | BOOL | 立ち上げ=保存 |
nTimeout | TIME VISION_ADS_TIME OUT | Timeout |
VAR_OUTPUT
bBusy | BOOL | 1=実行中. |
bError | BOOL | 1=エラーあり. |
nErrorId | UDINT | エラー情報 |
F_VN_StartRelWatchdog
Watchdogを起動します。Monitor時間は現在時間の相対時間になります。
VAR_INPUT
tStop | DINT | Stop time in us |
hrPrev | HRESULT | 前回の実行結果 |
Return Value
F_VN_StartRelWatchdog | HRESULT | 実行時間 |
F_VN_StopWatchdog
Watchdogを停止し、Runtime情報を取得します。
VAR_INPUT
hrStartWatchdog | HRESULT | The execute result of start watchdog function |
VAR_OUTPUT
nFunctionsMonitored | ULINT | 関数がMonitorされた回数 |
bEnFractionProcerrorssed | UDINT | 実数の処理時間 |
tRest | DINT | 残り時間 |
Return Value
F_VN_StopWatchdog | HRESULT | 実行結果 |
F_VN_PutTextExp
画像にテキストを入れる。
VAR_INPUT
sText | STRING | 入れたいテキスト |
ipDestImage | ITcVnImage | テキストを入れたい画像 |
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 | 1=画像の原点を左下に設定する |
hrPrev | HRESULT | 前回の処理結果 |
Return Value
F_VN_PutTextExp | HRESULT | 処理結果 |
ETcVnFontType
Offers font types.
Further information
ETcVnLineType
Offers line types
F_VN_DrawContours
この関数で画像にContoursを書きます。
- Contourは関数に渡すContainerにより変わります。
- この記事では、F_VN_ReadBarcodeExp()を使用しContourを検知します。
VAR_INPUT
ipContours | ITcVnContainer | Single contour (ContainerType_Vector_TcVnPoint2_DINT) multiple contours (ContainerType_Vector_Vector_TcVnPoint2_DINT) |
nContourIndex | DINT | 書きたいContourのIndexマイナス値ならすべて書くようにします。 |
ipDestImage | ITcVnImage | 書き込みたい画像 |
eFontType | ETcVnFontType | |
aColor | Reference To TcVnVector4_LREAL | Text color |
nThickness | DINT | Line thicknessマイナスならContourはFull-fitする |
hrPrev | HRESULT | 前回の実行結果 |
Return Value
F_VN_DrawContours | HRESULT | 実行結果 |
F_VN_ReadBarcodeExp
この関数はF_VN_ReadBarCodeのExpertバージョンです。この関数を使ってBarcoeを検知し、Codeの位置などを返します。
VAR_INPUT
ipSrcImage | ITcVnImage | ソース image Type:USINT elements, 1 channel・3 channel 3 channelの場合は内部の場合でGrayに変換します。 |
ipDecodedData | Reference ToITcVnContainer | Decodeされたデータ |
ipContours | ITcVnContainer | Code位置などの情報含むContainer返します。Type:ContainerType_Vector_Vector_TcVnPoint2_DINT |
eBarcodeType | UDINT | 検索するBarCodeの種類 |
nCodeNumber | DINT | 検知するBarCodeの数いま1つだけSupportできます。 |
eSearchDirection | ETcVnBarcodeSearchDirection | Barcodeの検索方向 |
hrPrev | HRESULT | indicating the result of previous operation |
Return Value
F_VN_ReadBarcodeExp | HRESULT | The execute result |
ETcVnBarcodeSearchDirection
TCVN_BSD_ANY | 最初は水平で、そのあとすんべて垂直方向。 |
TCVN_BSD_HORIZONTAL | 水平のみ |
TCVN_BSD_VERTICAL | 垂直のみ |
ETcVnBarcodeType
F_VN_ExportSubContainer_String
この関数を使用しContainer内のElementsを文字列として出力します。
ContainerType_Vector_String_SINTのみの2D Containersだけ有効です。
VAR_INPUT
ipContours | ITcVnContainer | Container ソース ContainerType_Vector_String_SINTのみ |
nIndex | ULINT | Container内のElements Index |
sText | STRING | Text 出力 |
nMaxLength | ULINT | |
hrPrev | HRESULT | 前回実行結果 |
Return Value
F_VN_ExportSubContainer_String | HRESULT | 実行結果 |
Implemention
前回の記事と同じく、RoboDKに関しては詳しく説明しませんのでProjectダウンロードしてみてください。
Configuration
こちらも前回と同じく、RoboDK 2DのCamera Simulation Interfaceを使用します。
そしてやり取りはTwinCAT TF6100 OPCUAを使います。
Flow
こちらはRoboDKとTwinCATのHandshake Flowです。
Barcode Reading Flow
こちらはBarCodeを読み込むためのFlowになります。
まずImagesを読み込む>BarCodeを検知>CodeをExport>テキストなどを描く>Imageを出力の流れです。
OPCUA Connections
下記のLinkでTwinCAT3のOPCUA TF6100を参考にしてください。
Beckhoff#Using TwinCAT TF6100 to startup OPCUA Server | (soup01.com)
Script to Create BarCode
こちらはランダムのBarCode生成プログラムです。
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
こちらのプログラムを写真読み込み>BarCode検知と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
こちらのScreenで4枚の画像情報を表示させます。
Sample Code
TwinCAT3/TwinCAT Project_Vision_Part2_ReadBarCode.zip at main · soup01Threes/TwinCAT3 (github.com)