OpenPLC ラズパイでPLC お題目・・・何か動かしてみる!!
何かネタをと言うことで、お国の技能検定のパレットに類似させて作ったアプリケーションが残っていましたので、これを利用して動きの見えるものを作って見ます。
最初のお題目(PLC動作仕様)・・・コンベアの手動動作(左右に動かすだけ)
①PB1をONでコンベアは左移動(PL1点灯)、左端(LS2)に達すると停止(RY1がOFF)
②PB2をONでコンベアは右移動(PL2点灯)、左端(LS1)に達すると停止(RY2がOFF)
③PB1、PB2の同時ONは動作しない仕様とする(マウスでは操作できませんが)
この手動動作仕様をラズパイ+OpenPLC・・プロジェクトに実装していきます。
・・・内容がちょっとラダー回路の作り方から外れてラダープログラムの組み方になっています。
自己保持回路、OR回路、AND回路は雑誌の内容、他諸先輩方々がラダーに関するインターネットホームページ等で記載されておられますので、そちら参照していただければと思っています。
店主の勝手なプログラムの組み方ご紹介(IEA61131-3ぽいかも??)になります。
OpenPLCエディター プロジェクトの作成
プログラムどういう組み立て方がいいのか思案していましたが、雑誌の様に一つのPOU(プログラムユニット)に書いてもしれているのですが、少しはIEA61131-3の指針に合わせて(なってないかもしれませんが)作って見ます。また実IOは各POUで必要なものをラベル定義すればいいのですが、いったん構造体定義のグローバル変数としてインターフェースすることにします。こうしておくと実権レベルではあまり意味ありませんが、FA現場レベルでは実IOの変更等の時非常に便利になります。(全プログラムに渡って変更の必要がなくなる・・・このインターフェース部分だけでOK)
1:プロジェクトの作成でとりあえず「RaspberryPiCnv」と言うフォルダーのプロジェクトで作成して見ます。名前は適当です・・・・そして最初は「Create a new POU」ダイアログで「Language」として「LD」を選択してOKします(最初はプログラムしか選択できません)。
2:「Project」タブで「+」をクリックして「DataType」を選択、作成された「datatype0」の名前を「sIO」に変更します。この「sIO」の画面で「Derivation」の項目を「Structure」を選択すれば構造体の作成になります。ここに各デバイスのラベルを種類「BOOL」で作成して定義を完成します。
3:この定義された構造体をProjectタブの一番上にある「Unnamed」のアイコンをダブルクリックして「G_sIO」としてグローバル変数へ登録します。(ここで実際にメモリーに割り付けられます)
4:Projectタブで最初に作成されたプログラム(POU)「program0」をまず入力に関するデバイスのインターフェースとして「InterfaceIN」と名前を変更します。このプログラムにはラベルで「Local(Class)・BOOL(種類)」として「in_0~15」を「%IX0.2~100.3」(ラズパイ+ArduinoUNOのLocation)に合わせて定義に加えて「G_sIO」を「External(Class)・sIO(種類)」として定義しておきます。プログラムラダーは単純に設定「in_0」がONすればコイル「G_sIO.LS1」がONと言う具合で作成します。
*%IX0.2~%IX100.3にした理由
<その1>での内容で%IX0.0と%IX0.1はビット反転でしたので、ここでは%IX0.2~使用します。ラズパイ+UNOだけで実験操作(ブレッドボードにIOを取り出して実験操作)する際は各ビットにラズパイの場合ラズパイボードの3.3V、UNOの場合UNOボードの5Vを接続すればON状態(論理が1もしくはH、True)になります。入力を安定させるために各ビットは雑誌の基板の様に10KΩ程度でプルダウンしてください。また出力は1KΩ程度を通してLEDを接続すれば点灯すると思います。
5:出力をイメージの構造体「G_sIO」から実出力に転送するプログラム「InterfaceOUT」を追加して、ここに「InterfaceIN」と同様にグローバル変数構造体「G_sIO」と実出力「out_0~15」を定義します。プログラムは接点「G_sIO.RY1」がONするとコイル「out_0」がONになる回路です。
ここまでで実IOのイメージがグローバル変数「G_sIO」でアクセスできる様になりました。
こうしておくと、プログラム上で実入出力が変更になった場合でもここだけの修正で完了します。
6:次はコンベアを移動させる部分のプログラムを作成します。名前は「ConverObj」としています。
この制御オブジェクトで使用する変数で外部へ公開するステータスを構造体で作成して、グローバルにしておきます。変数の内容はこのオブジェクトの状態を他のプログラム(オブジェクト)に公開する内容のみです。全体の組み方は店主の手法になりますが(これがベターな訳ではありません・・・あくまでも参考と言うことで)、オブジェクトの動作する要求はオブジェクト自身のプログラムで取得する(要求信号は要求する側がグローバル変数で出力)。そして、オブジェクトの状態はグローバル変数で他のオブジェクトが取得できる様にしておく。本来ならIEC6113-3で定義されている「INPUT」「OUTPUT」の定義がラダーで使えてPOU内で外部が参照できるグローバル的な変数定義ができればいいのですが、ど~~も使い方がよくわかりません。「INPUT」「OUTPUT」の定義はファンクションやFBでの使用に限定されているのかもしれません。
上記のこの2点で基本的にはラダーでの2重コイルによるバグを回避できるかと(必要に応じては2重コイルも使いますが・・・)思います。プログラムの構成はまず最初に他からの要求(リクエスト)を取得して状態を生成し、最後のほうで出力をします。このパターンをFB(ファンクションブロック)等で作っておけばさらに使いやすくなるかもしれません。FBはC++等でのクラスに相当するイメージでしょうか・・・・設計図FBを作っておいて各POUで実体化するイメージです。
で、このプログラムの作成ですが変数は実行状態を動作中と状況を外部から参照できる様に構造体として「sCnv」を作成してグローバル変数にしておきます。内容は要求(RQ)があれば実行中(run)をONして、リミットが入るまでリレーをONするだけです。
[後で実験してみた方法(グローバル変数としないでPOU間で共有)
外部に公開したいコイルを「%MX0.00」等でローカル変数として実内部メモリーに割り付けても可能です。ただ、受け取る側でも「%MX0.00」をローカル変数にしないといけないため、この情報は「%MX0.00」という名称でしか使えません・・・・バグ(タイプミス)の温床になると思われます。
7:操作の部分を組んでいきます。手動操作なので名前は「ManuCont」にします。そしてここで要求出力を外部に通知する変数「G_sManu」を構造体「sManu」として作成です。内容は単にコンベア左動作、右動作のリクエストだけですが・・・
その後実際のプログラムとして、押しボタンが押されたら動作要求のリクエストを出力します。
8:最後に動作状態を操作者に伝えるランプの回路です。コンベア操作に対して動作中ランプがONするように作ります。名前は「LampObj」としてここは外部に通知するものはなく、他のプログラムの状況でランプを点灯するだけなので、このプログラムのデーターはなしです。ただ参照するオブジェクトのデーターとIOのデータは必要なので、「External」で「G_sCnv」と「G_sIO」を定義しておきます。回路はコンベア動作中ならランプ点灯して、リミットに達したらFF1sec(1秒フリッカーコイル)で点滅させます。
9:最後にプログラムの登録です。Projectタブにあるリソース「Res0」のアイコンをダブルクリックして、「Instances」の部分に追加していきます。実行処理の並びは上から「InterfaceIN」、「MaanuCont」、「ConverObj」、「LampObj」、「InterfaceOUT」の順です。別にどの順でも一応はラダー回路なので動作すると思いますが、ものの流れとして順序を決めています。
これで「Generato program…..」(赤い下方向の矢印アイコン)してみて
OKな様ですので、「ManuCnv」として保存します。後はラズパイのサーバーに読み込ませて実行させて見ます。
<その2>のまとめ
その2でのマニュアルコンベア動作のプログラムは下記にあります。
<ラズパイ・Arduino+UNOのIOピンとラベルの割り付け>
IOを接続しての確認は下記のピン割り付けです。ラズパイだけでの確認はラズパイ入力にラズパイの3.3Vを接続するとONになります。またUNOなしのラズパイのみで実験する場合、上記プログラムはラズパイ+UNOでの記述なので、UNOに関する実入出力(%IX100.N、%QX100.N)の実IOのLocationをクリアしておかないとそのIOが無いのでボードにUPLOADした際に怒られました。
クリアすることで、システム内部の適当なメモリーに割り付けられます。
プログラムを記述していて、注意すべき点は(よくミスった点)
1:変数のスペルは大文字、小文字が区別される
2:変数の登録は必ずPOU単位で必要(扱うグローバル変数もExternalで定義必要)
3:リソースでタスクへのPOUの登録を忘れない・・・
4:ローカルな変数は適当なメモリー割り付けで他から操作できないように隠蔽
(プログラムが大きくなると・・実アドレスIOは謝って他のPOUで使ってしまうかも・・・)
でしょうか・・・
OpenPLCエディター使用での注意点として、実IOのLocation等を入力時にダブルクリック等を変にしてしまうとOpenPLCエディターが勝手に終了してしまう場合(バグ??)があるようです(まめに保存ボタンで保存しておかないと、実行のファイルを作成してもプログラム内容は保存されていません)。
あと、ラズパイを使ったシステムでは実際のところ電源のぶち切りを考慮しておかないと、OSが起動できなくなる等の問題が発生します。シャットダウンなしに電源を切る(落ちる)、シャットダウン中に電源が落ちるなどの場合SDカードに問題を引き起こします。この辺りはよく知らないのですが、インターネット上にはいろいろ諸先輩方々がまとめられていますので、最後辺りにまとめられたらまとめてみたいと思います。・・・簡単な制御なら実行速度はかなり遅くなりますが、ArduinoMEGA(IO点数が多い)がいいかもデス。市販のPLCも安くなりましたが、プログラム作成環境が高価ですから・・
<その3>・・・自動動作編へ続く
OpenPLCエディターで気になった点
OpenPLCエディターの出力する「ストラクチャードテキスト(ST言語でラズパイで実行されるプログラム)」に関して少し気になる部分がありました。ラダー回路からOpenPLCサーバーに渡すストラクチャードテキストへの変換ですが、下記のフリッカーの回路の場合で見つけました。
通常(特殊かも)もう少し簡単にコイル1個減らして下記の様にいつも書くのですが、この場合だとうまく動作しません。
この場合、TON0で1秒ごとの1スキャンだけのPls1sec(パルスのON)が発生すると、下の回路では「Pls1sec」がONして「FF1sec」がOFFなので、A接点の「Pls1sec」とB接点の「FF1sec」を経由して「FF1sec」のコイルがONします。その次のスキャン以降では「Pls1sec」がOFFで「FF1sec」ONなので、A接点の「FF1sec」とB接点の「Pls1sec」を経由して「FF1sec」のコイルは自己保持でONを継続します。
しばらくして再度「Pls1sec」がONすると、今自己保持を支えているB接点の「Pls1sec」がOFFになるので(FF1sec」ON上の「Pls1sec」ONはORの合成点で同じTrueなので無視)「FF1sec」のコイルは自己保持解除されます。その次のスキャン以降は頭のA接点「Pls1sec」、A接点「FF1sec」ともにOFFなので「FF1sec」のコイルはONするこつはありません。(この後の「Pls1sec」ONでまた自己保持します。うまく動作しないので、この回路でのST言語に翻訳されたこの部分を探してみると下記の様になっていました。(Stへ翻訳されたファイルは拡張子が「.st」です)
TON0(IN := NOT(Pls1sec), PT := TIME#1000ms);
Pls1sec := TON0.Q;
FF1sec := NOT(Pls1sec) AND (FF1sec OR Pls1sec) OR NOT(FF1sec) AND FF1sec;
このままではうまく動作しませんでしたので、下記の様に変更すると動作しました。
FF1sec := (NOT(Pls1sec) OR NOT(FF1sec)) AND (Pls1sec OR FF1sec);
OpenPLCエディターの解釈変換の違いと思われますが、回路を組むとき少しトリッキーな使い方するとうまく動作しない可能性があります。ご注意を・・・できるだけ1回路づつ分けて書く方がいいようです。(トリッキーな使い方はするな!!って意味かもしれません)
市販メーカーのPLCではこんなことはありませんが・・・というか動作しなかった経験がないです。