みなさんこんにちは。このシリーズはBeckhoff TwinCAT3のTF7xxx Visionライブラリのメモになります。きっかけはいまRoboDKを使用しロボットの勉強を始めたところで、Visionもやってみたいと思って、最初はOpenCVを試してみようと考えていました。調べたらBeckhoffにもVisionライブラリがあり、まずある程度Visionはどんな流れになるかを把握し、そのあとOpencvに切り替えるかしないかになりました。
TF7xxxのライブラリを使用するには、OOPプログラミングやPointer/Refプログラミングについての理解がMustなので、基礎が問われるところになりますね!
About TF7000 Vision
では、まずTF7000はどんなものなのかを簡単にまとめます。TF7000はBeckhoff社が提供する画像処理ライブラリで、UserがIEC61131-1準じるPLCプログラム言語からVisionのリアルタイムアプリケーションを組むことが可能になります。
カメラのCalibration・セットアップ・DebugなどもTwinCAT環境で完結できます。
注意するのはTF7000 Supportできるのは GigE Vision interfaceのカメラのみです。
あとTE2000と統合できるようですが、これはまた少し深く理解してから説明します。
TF7000 Vision使用すると実装できるアプリケーションは、いま頭に浮かぶのは:
- 間隔を計測
- 物体の色・位置・状態を検出
- QRCode/Bar Codeの読むこむ
- Cloudに接続し
つまりそのようなことを一括TwinCATだけで統合できます。
License for Camera TF700x
TF7000はBasicライセンスで2つのカメラだけになります。
もし追加したいなら数により、TF7001/TF7002/TF7003を使用できます。
License for Library TF7xxx
次は画像処理で使うアルゴリズムに関するライセンスも別です。
- TF7100はTc3_VisionライブラリのBasicバージョンになります。Filters・色処理・カメラのパラメタ書き込みなどができます。
- TF7200には2D Matching機能が追加されます。多分Pattern認識などかな?
- TF7250は1D/2Dのコードを読み取る機能です。(BarCodeやQRCodeなど)
- TF7300は距離測定などの最適化・エッジ検出のようです。
Installation.
下記のLinkにアクセスします。
TF7100 | TwinCAT 3 Vision Base | Beckhoff Worldwide
Software DownloadのところでExeをダウンロードしましょう。
ダブルクリックでSetupをスタートします。
英語を選んで、OK。
Nextします。
ライセンス同意し、Nextします。
情報を入れて、Nextします。
Completeを選び、Nextします。
Nextします。
Installをクリックし、インストールを始めます。
しばらく待ちください。
完成です!
System Requirment
IPC
Camera
Software
SetUP A Vision Devices
申し訳ございませんが、自分はカメラを持っていませんのでこの部分をSkipさせていただきます。カメラもし手持ちくる日があれば、また別の記事で説明しますね★
API
TwinCAT VisionライブラリはAPI提供しており、IEC61131-3準じるプログラム言語使用し画像処理することができます。いまからAPIの中にいくつの重要なコンセプトを紹介します。
Interface Pointer
APIから2種類はよく使うInterface Pointerがあります。それは:
- ITcVnImage:画像処理用
- ITcVnContainer: 複数の部品を保存するvector
たとえば下記の例でF_VN_CreateImage() 関数を使用し800×600、1チャンネルの画像を作成します。
IF bCreateImage THEN hr:=F_VN_CreateImage( ipImage ,nWidth:=800 ,nHeight:=600 ,ePixelType:=ETcVnElementType.TCVN_ET_SINT ,nChannelNum:=1 ,hrPrev:=hr ); IF hr <> 0 THEN bError:=TRUE; END_IF bCreateImage:=FALSE; END_IF; |
そのような画像が作成されますね。
Some Rules
ここでプログラミングするときいくつかのヒントを書きます。ヒントとはいえ、BeckhoffやCodesysでプログラムするときの基本ですので、気楽にみてください。
1-Method呼び出す前のPointerCheck
ITcVnImage などのPointer interfaceのMethodをCallする前に、必ずPointerが有効かどうかをCheckしてください。もしMethodがCallされたPointerが無効であればRuntimeが止まってしまうので。一番簡単な方法は0かどうかをCheckしますね。
//GetInfo IF ipImage <> 0 THEN ipImage.GetImageInfo( stImageInfo:=ImageInfo ); END_IF; |
2- PointerのMethodよりF_VN Functionを使用
TwinCATのTC3_VisionライブラリはF_VN_xxx Functionが提供しており、ITcVnImage のMethodはすべて代用ではないが、該当するMethodがあればF_VN_XXX関数を使ってください。理由はF_VN_XXX関数はPointerが有効かどうかをCheck機能がついているからです。
F_VN_GetImageInfo( ipImage ,stImageInfo:=stImageInfo_1 ,hrPrev:=hr ); |
3- Pointer IntefaceをVAR_INPUTしましょう。
Function BlockやPOUでPointer Interfaceを使用するときはVAR_INPUTを使いましょう。みなさんも知ってると思いますが、FBやPOUでは変数値をCycle完了したあとにも保持します。そのため、私たちは次のCycleでアクセスするメモリエリアが使用できるかどうかを確信持つことができます。
VAR_INPUT ipSrcImage: ITcVnImage; END_VAR //… functional code ipSrcImage := 0; |
4-使わないPointerはリリースしましょう
VARでPointer interfaceを定義しプログラムを使用しますね。Pointerも使わないとき(例えば画像処理プログラム完了したあと)にMemoryを解放しましょう。
例えば以下の例ように:
VAR ipImageWork: ITcVnImage; END_VAR //… functional code hr := FW_SafeRelease(ADR(ipImageWork)); |
実際関数 FW_SafeRelease()はこのようなコードを実行しています:
IF binit THEN IF ipImage <> 0 THEN ipImage.TcRelease(); ipImage:=0; END_IF binit:=FALSE; END_IF |
5- VAR_OUTPUTでInterface Pointer定義やめましょう
ここで簡単に言いますと、FBやPOUはCycle実行終わっても変数値が保持するとみなさんもすでに知ってると思います。それはVAR_INPUTだけではなく、VAR_OUTでも同じです。それはなんの問題になるかというと1つCycleの前持ってたVAR_OUT変数のメモリアドレスがまだ有効かどうかの確信がありません。
無効なメモリ領域にアクセスすることによりRuntimeが止まることになりますので、できればVAR_INPUTでInterface Pointerを定義しましょう。
HRESULT
すべてのTwinCAT Vision 関数にはHRESULT も戻り値があり、関数の実行結果を示します。このコンセプトはFile 作成などするときと似ています。
以下は関数が実行成功したときのHRESULTの見方です。
こちらはSuccess-Codes値の意味一覧です。
以下は関数が実行失敗したときのHRESULTの見方です。
こちらはFault-Codes値の意味一覧です。
SUCCEEDED()関数を使用し関数の実行結果をCheckすることができます。
PROGRAM MAIN VAR hr : HRESULT := S_OK; END_VAR IF SUCCEEDED(hr) THEN //code END_IF |
Image processing
ITcVnImage InterfaceはTc3_Visionライブラリの画像処理Interfaceの基本です。最後のExampleでは自分がそのITcVnImage を使用し画像をTwinCATに読み込み、QRCodeを探します。
ITcVnImage InterfaceはいくつかのMehtodが提供しており、画像の横縦やSize、Raw Dataにアクセスすることができます.
Container
次はContainerです。ContainerはTc3_Visionというより、TwinCATの中でもちょっと特別な部品だと思います。なぜかというとContainerに保存してるのは複数の部品でVectorとしてまとめられているからです。Containerに入ってる部品(いわゆる変数)は可変でプログラムより自由に変わります。注意するのは中に入ってるもののデータ・タイプは同じにする必要があります。
いまの時点でContainerのことを難しく考える必要がなく、大きさが自由変更できる箱だと思えばよいです。
PROGRAM MAIN VAR hr : HRESULT; ipContainer : ITcVnContainer; END_VAR |
以下の例でContainer がどう動くかを簡単に説明します。
もちろんContainer のCopyやContainer の中に更にContainer を格納するなどの上級の使い方があるらしくて、自分もまだそこまでわかりませんのでとりあえず一番基本なところから行きます。
- File Systemから写真をTwinCATに読み込まれせます。
- 関数を使用しQRCodeを検索します。
- DecodeされたデータがContainersのメモリに格納されます。
- 関数を使用しContainerのDataを取り出します。
- DataをUser Programに使用します。
- Container メモリ、画像メモリを解放します。
Function/Function Block
TwinCAT Tc3_Visionは数多くのFB/FCありますので、全て説明するには不可能ですので今回Exampleで使われるもののみ説明します。
FB_VN_ReadImage
このFBを使用することでFile Systemから画像をTwinCATに読み込むことができます。
VAR_INPUT
sFilePath | STRING | 画像のFull Path |
ipDestImage | Reference To ITcVnImage | return image |
bRead | BOOL | 立ち上げ=画像読み込む |
nTimeout | TIME VISION_ADS_TIME OUT | Timeout |
VAR_OUTPUT
bBusy | BOOL | True=実行中 |
bError | BOOL | True=エラーあり |
nErrorId | UDINT | そのErroID |
F_VN_ReadQRCode
ITcVnImage内でQR Codeを検知する関数です。
VAR_INPUT
ipSrcImage | ITcVnImage | Soruce Image、RGB可能で内部でGaryに変換します。 |
ipDecodeData | Reference To ITcVnContainer | DecodeされたDataのContainer |
hrPrev | HRESULT | 一回前の関数実行結果 |
Return Value
F_VN_ReadQRCode | HRESULT | 実行関数結果 |
F_VN_ExportSubContainer_String
SubContainerから文字列をExportする関数です。
VAR_INPUT
ipContainer | ITcVnContainer | Container type with ContainerType_Vector_String_SINT |
nIndex | ULINT | |
hrPrev | HRESULT | 一回前の関数実行結果 |
sText | STRING | Exportされた文字列 |
nMaxLength | ULINT | 最大出力文字列数 |
Return Value
F_VN_ReadQRCode | HRESULT | 実行関数結果 |
Implemention
Configuration
この例では自分がRoboDKをOPCUA Clientとして使用し、RoboDKはCamera シミュレーション機能ついておりそのおかけでCamera実機なくても連動Pick>撮影>Placeの動作ができます。
TwinCAT SideではTF6100を使用しOPCUA Serverを立ち上げ、TF7250を使ってRoboDKが撮った写真をTwinCAT内に読み込み、QRCodeを検知します。
この記事ではRoboDKの部分はあまり詳しく説明しませんので、最後にProjectをダウンロードしてください。Python Scriptで実装していますだけなので。
Connect>Simulate 2D Cameraするとカメラを模擬できます。そのあとはOpenCVでもよいし、アクセス方法は自由自在です。
Flow
以下の図はRoboDKと連動したFlowです。
Add library
まずLibraryを追加します。
References>Add Libraryします。
Tc3_Visionを検索しOKします。
OPCUA Connections
下記のLinkからTF6100のOPCUA Server立ち上げ方法を参考にしてください。
Beckhoff#TwinCAT3 TF6100 OPCUA _Part1_Server立ち上げよう | (soup01.com)
Script to Create QRCode
from robolink import * # RoboDK API from robodk import * # Robot toolbox import qrcode import tempfile #———————————————- # Create the QR code data = mbox(“QRCode inside the text;”, entry=”Beckhoff.com”) if data is None or data is False or type(data) is not str: # User cancelled quit() img = qrcode.make(data) #———————————————- # Save to file filename=None if filename is None or filename == ”: filename=r”C:\Users\root\Desktop\qrcode1.png” img.save(filename) import os.path if not os.path.exists(filename): quit(-1) # Import in RoboDK RDK = Robolink() img_item = RDK.AddFile(filename) if not img_item.Valid(): quit(-1) |
プログラム
以下のコードSmapleはFile SystemからImageを読み込み、QRCodeを検知、最後は情報をTE1800で表示させます。
VAR
VAR //FB FB_Images:FB_VN_ReadImage; R_TRIG1,R_TRIG2 :R_TRIG; TON:TON; hr,hr2:HRESULT:=S_OK; index:ULINT; //Pointers ipImage:ITcVnImage; ipDecodedData:ITcVnContainer; stImageInfo_1:TcVnImageInfo; ImageInfo:TcVnImageInfo; //Trigger Devices read:BOOL; readQR:BOOL; binit:BOOL; safetyInit:BOOL; //Image Information width:UDINT; height:UDINT; sText:STRING; 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)); safetyInit:=FALSE; read:=FALSE; readQR:=FALSE; hr:=0; hr2:=0; END_IF //GetInfo IF ipImage <> 0 THEN ipImage.GetImageInfo( stImageInfo:=ImageInfo ); END_IF; F_VN_GetImageInfo( ipImage ,stImageInfo:=stImageInfo_1 ,hrPrev:=hr ); //Robot Status Handshake R_TRIG1( CLK:=GVL_OPCUAServer.Node.Command2 = 1 OR GVL_OPCUAServer.Node.Command2 = 3 ); R_TRIG2( CLK:=GVL_OPCUAServer.Node.Command1 = 1 ); IF R_TRIG2.q THEN GVL_OPCUAServer.Node.Command2:=0; height:=0; width:=0; sText:=”; END_IF IF R_TRIG1.Q THEN GVL_OPCUAServer.Node.Command1:=0; END_IF //Read the Image //Trigger the Image read Function FB_Images.bRead:=read; FB_Images( sFilePath:=’C:\images\Images.png’ ,ipDestImage:=ipImage ); TON( IN:=readQR ,PT:=T#0.1S ); //Trigger the QR Read Function IF NOT FB_Images.bError AND read THEN readQR:=TRUE; END_IF //get the image information IF readQR THEN IF ipImage <> 0 THEN ipImage.GetHeight(nHeight:=height); ipImage.GetWidth(nWidth:=width); END_IF; END_IF; //read the QRCode IF TON.q THEN hr:=F_VN_ReadQRCode( ipSrcImage:=ipImage ,ipDecodedData:=ipDecodedData ,hrPrev:=hr ); //Get the Text from Container IF SUCCEEDED(hr) AND ipImage <> 0 THEN hr:= F_VN_ExportSubContainer_String( ipContainer:=ipDecodedData ,sText:=sText ,hrPrev:=hr ,nIndex:=index ,nMaxLength:=255 ); IF SUCCEEDED(hr) THEN read:=FALSE; readQR:=FALSE; FW_SafeRelease(ADR(ipDecodedData)); END_IF END_IF; END_IF; |
Visualization
Run Button:RoboDKに送るのStart Command
Snap!:Snap RoboDKに送る撮影コマンド
Read QR Data:画像を読み込みます。
Clear QR Data:Pointer・画像・Flagをクリアします。
Sample Code
TwinCAT3/TwinCAT Project_Vision_Part1.zip at main · soup01Threes/TwinCAT3 (github.com)
Result
お疲れ様ですー