APDahlen Applications Engineer
多くのPLCプログラマーにはArduinoの経験がある
PLCプログラマーの多くは、マイクロコントローラのクラスを修了することからエレクトロニクスの道を歩み始めます。最初のプログラミングは、おそらく古典的な「hello world」またはArduinoに取り付けたLEDを点滅させるものでしょう。これらのスキルは、プログラマブルロジックコントローラ(PLC)を含む、一見無関係に見えるプログラミング環境の優れた基礎となります。
この予備知識を活用して、Arduinoのmap()関数から同様のラダーロジック (LL:Ladder Logic)ベースのmap()関数に移行します。PLC内部では、このmap()関数はユーザー定義関数ブロック(UDFB:User Defined Function Block)として実装されます。一般的な Schneider ElectricのModicon PLCを使用して説明します。このレッスンは一般的にほとんどのLLプログラミングPLCに適用できますが、具体的な実装の詳細は異なります。
このアプリケーションの実験装置を図1に示します。ここでは、SchneiderのModicon PLC( TM221CE24T )がBannerのタッチボタン( K50 Pro )を駆動しています。
図1: Bannerのタッチボタン( K50 Pro )を駆動するSchneiderのModicon PLC
ArduinoのスキルをPLCに応用
PLCの実装を探求する前に、まずArduinoの視点から3つの基本概念を探ります。これには以下が含まれます。
- 関数の構成要素のレビュー
- ドット記法の概念
- map()関数の数学
これらのC言語プログラミングの概念をレビューした後、その基本がModiconベースのUDFBにそのまま適用できることに気づくでしょう。おそらくラダーロジックとC言語は、当初考えていたよりも近い関係にあることが分かるでしょう。将来的には、この拡張された視点が構造化テキスト言語(ST:Structured Text)を使用した PLC プログラミングの完璧な基礎となることに気づくかもしれません。
関数の構成要素
関数はC言語の一連のステートメント(文)をカプセル化する役割を果たすことを思い出してください。おそらくあなたは、LEDをN回点滅させるArduinoの関数をプログラムしたことがあるでしょう。その関数はblinkLEDのような名前だったでしょう。その関数には、LEDを点滅させる回数を示すNなどのパラメータがあったはずです。他にも、LEDのオン時間とオフ時間のパラメータを指定していたかもしれません。I/Oピンをパラメータ化したこともあるでしょう。
void LEDblink(int pin, int N, long msOnTime, long msOffTime) {
pinMode(pin, OUTPUT);
for (int i = 0; i < N; i++) {
digitalWrite(pin, HIGH);
delay(msOnTime);
digitalWrite(pin, LOW);
delay(msOffTime);
}
}
技術的なヒント: C言語の関数の戻り値の型は1つだけですが、間接アドレッシングを使用することで、項の数を増やすことができます。間接アドレッシングでは、配列や構造体のアドレスを関数に渡します。この関数は、配列または構造体内のすべての要素にアクセスできます。要素の総数は、関連するマイクロコントローラのメモリとアーキテクチャによって制限されます。
構造体とドット表記
PLCに移行する前に、C言語プログラミングで使用されるドット記法を調べておきましょう。次のコードを検討してください。
typedef struct { // Structure definition
float minVoltage;
float maxVoltage;
float deviation;
} Status;
Status CellStatus; // instance of the structure
コードの最後の行がStatus構造体のインスタンスを生成していることに注目してください。この構造体にはminVoltage、maxVoltage、およびdeviationのメンバが含まれます。構造体内の個々のメンバにアクセスするには、ドット記法を使います。例えば以下の通りです。
CellStatus.minVoltage = 13.8;
float maxVoltage = CellStatus.maxVoltage;
いずれの場合も、構造体の名前の後にドットが続き、その後に対象のメンバが続きます。
Arduino map( )関数
PLC UDFBに取り組む前に、最後に検討すべきもう 1つの概念があります。具体的には、PLCに実装する前に、Arduinoの視点からmap()関数の数学的演算を検討します。
Arduinoのmap()関数は、直線の方程式に関する代数の応用です。
y = mx + b (傾きと切片方程式)
y – y_1 = m(x – x_1) (点と傾き方程式)
Arduinoでは、ArduinoのADC(Analog To Digital Converter)のアナログ入力をマッピングするためにmap()関数を使ったとします。
int rawADCval = analogRead(0);
int HumanReadableVoltage = map(rawADCval, 0, 1023, 0, 5); //Assume a 5 VDC ADC reference
これは、0~1023のADC出力が0~5V DCにマッピングされるということです。しかし、電圧0、1、2、3、4、および5しか許容されないという大きな問題があります。これは整数型データ形式の制限であり、浮動小数点数型を使用することですぐに修正できます。
map()関数自体は以下のように定義されます。
long map(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
map()関数をよく調べてみると、この関数が点と傾き方程式を実装していることが分かります。
y \ – y_1 = m(x\ – x_1)
ここで、
m = \dfrac{out\_max - out\_min} {in\_max - in\_min}
したがって、以下のようになります。
y = \dfrac{out\_max - out\_min}{in\_max - in\_min} (x - in\_min) + out\_min;
Arduinoのmap()関数を浮動小数点型に変更
個人的には、浮動小数点型の関数を作成したいと考えています。これは、関数のデータ型を以下のように変更することで簡単に実現できます。この小さな変更によって、先に述べた整数型の演算の問題が解消さ れます。
float fmap(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
float rawADCval = analogRead(0);
int HumanReadableVoltage = fmap(rawADCval, 0.0, 1023.0, 0.0, 5.0);
ModiconのUDFB(User Defined Function Block、ユーザー定義関数ブロック)入門
ここまでが、以下を含むマッピング(スケーリング)関数に関する基本的なプログラミング概念のレビューです。
- 関数の各部分
- 関数の構成要素の名前
- 構造体
- データ型の制約
- ドット記法
- 点と傾き方程式
Modicon PLC用のラダーロジックベースのUDFBを作成する準備ができました。UDFBを図2に示します。UDFBの定義は、2つの部分で構成されています。UDFB名、ブール型入力、およびパラメータは、図2の下部に示されています。ラダーロジックは、図2の上部に示されています。
図2: Modicon FMAPのUDFB(ユーザー定義関数ブロック)
イネーブル(EN:Enable)とイネーブル出力(ENO:Enable Output)は、UDFBをラングに掛けるために使用
当然のことですが、FMAP UDFBの図3のRung1のインスタンス化に示されているように、全てのラダーロジックエレメントは「ラングに掛けられ」ます。この重要な動作は図2の下部で確立(定義)されています。ここでは、イネーブル(EN)とイネーブル出力(ENO)のペアが示されています。図2のRung0では、ENOがEN信号のコピーであることが分かります。別の言い方をすれば、EN信号があればブロックのENO信号は真と評価されます。将来的には、このようなブロックを同じラングに多数配置し、下流の各ブロックが EN信号のコピーを受け取るようにすることも可能です。
技術的なヒント: UDFBにはいくつかの 「ラングに掛けられた 」入出力のペアがあります。
例えば、従来のENとENOのペアです。このブロックは、それがイネーブルになっている場合にのみ機能し、ENOはEN信号の直接的なコピーです。
レベル感知ペアはEN信号とVALID信号を含みます。ブロックはイネーブルの時のみ機能します。UDFBが指定された動作を完了すると、VALID信号が発生します。方向制御値(DCV:Directional Control Value)が良い例です。UDFBはEN信号を受信するとDCVにトグル信号を送出します。VALID信号は、監視対象のシリンダが適切な位置に移動した後しばらくして発生します。BUSY出力信号が含まれることもよくあります。
エッジ感知UDFBはトリガ(TRIG)とDONEのペアを含みます。TRIG入力を受け取ると、UDFBは所定のタスクを実行します。タスクが完了すると、UDFBはDONE出力パルスを発生します。レベル感知UDFBと同様に、エッジ感知UFDBもBUSY出力信号を備えています。さらに、多くの場合、TRIGを元に戻すためのABORT、またはRESET入力を備えています。例として、サイモンセズ(Simon Says)のようなゲームを考えてみましょう。エッジ感知UDFBをコンピュータの再生に使用することができます。完了すると、ユーザー再生用に別のエッジ感知UDFBがトリガされます。
この従来のUDFBの動作はEN信号に基づく
EN信号は、UDFBのさまざまな計算を有効にするために使用されます。図2のすべてのラングでは、各ラングの先頭にEN接点があります。UDFBの出力が更新されるのは、それがEN信号ラインを介してイネーブルされた場合のみです。これは、EN信号がないときに最後の値が保持されるような暗黙のメモリ動作があるため、混乱する可能性があります。これは、従来のUDFBが更新されない場合に最初にテストすべきことの1つです。
技術的なヒント: 従来のUDFBに付随する暗黙のメモリは、有益な属性となり得ます。周期的なパルスでUDFBの出力を瞬間的に更新することができます。例えば、プロセスコントローラで使用されるUDFBは、PPS(Pulse Per Second)信号で出力値を更新し、保持するように命令されることがあります。
UDFBパラメータ
図2は、UDFBに関連するパラメータを示したものです。「パラメータ」という用語は、おおまかにはUDFBに関連するメモリ構造として解釈することができます。これはいくつかの理由から重要な違いです。
-
パラメータ型は、すべての非ブール型UDFB出力の入力と出力の両方として機能します。このUDFBでは、すべての線形方程式の計算に浮動小数点型を使っています。
-
図4に示すように、「パラメータ」にはドット表記を使ってアクセスします。
-
UDFBがインスタンス化されると、各インスタンス化はメモリと関連付けられます。これは、この記事で紹介した構造体 「Status CellStatus; 」と同じような働きをします。この動作は、各UDFBが独自のパラメータと内部変数をどのように持っているかも説明しています。
UDFBの数学的演算
図2のRung1とRung2は、この記事の前半で検討したように、FMAP関数を実装したUDFBです。2つのオペランドを使った演算を行っていることに注意してください。例えば、差分出力(D_OUT)はOUT_MAXとOUT_MINの差です。同様にD_IN = IN_MAX - IN_MINです。傾き(M)はD_OUT / D_INとして計算されます。残りの操作は、与えられた入力Xに対するYを解くために傾きと切片方程式(1次方程式)の計算を続けることです。
UDFBのインスタンス化の方法
この記事ではArduinoの紹介から始めました。具体的には、1次方程式、関数の要素とドット表記について説明しました。この時点で、関数をUDFBとうまく比較しました。また、UDFBはパラメータという用語を自由に使って入力と出力の両方を表すということを理解した上で、C言語の関数とUDFBの仕組みの類似性を見てきました。最後に、各UDFBのインスタンス化は、それ自身の構造体のようなメモリオブジェクトに関連づけられることを示しました。これでUDFBをインスタンス化し、ドット記法を使う準備ができました。
FMAP UDFBの実際のラングに掛けられたインスタンス化を図3のRung1に示します。UDFBはラングRung0とRung1に示される演算ブロックによってサポートさ れます。例えばRung1の最初のブロックは、定数値0.0を%FMAP0.IN_MINメモリロケーションにロードします。インスタンス化されたUDFBに関連するメンバ(変数)にアクセスするためにドット演算子が使用されていることが分かります。
このドット表記法とメモリ構造を考慮すると、C言語がオブジェクトを扱うのと同様に、SchneiderのMachine ExpertはUDFBを扱う、と確実に言うことができます。どちらのプログラミング言語もカプセル化を行います。
この例におけるUDFBの目的は何でしょうか?
このUDFBの最大の目的は、Modicon PLCのアナログデジタルコンバータ(ADC)の値を人間が読める値に変換することです。図3のRung0では、ADCの実際の値が533カウントであることが分かります。Modicon ADCは 0~10V DCのフルスケールを0~1000の値で供給するように設定されていることを思い出してください。Rung2では、UDFBのすぐ右側に演算ブロックがあります。その値は浮動小数点値5.33にマッピングされていることに注意してください。
確かに、このUDFBの例は設計過剰です。確かに、単純に10で割る演算ブロックを使うこともできたでしょう。しかし、電圧が物理的なシステム圧力を表し、1~7V DCが30~90psiに相当するとしましょう。この場合、2つのFMAP UDFBを使用する必要があります。1つ目は信号を人間が読める形式に変換します。2つ目は、この人間が読める浮動小数点値を対応するpsiでマッピングします。この2つのブロック構造は、将来の技術者にとって読みやすさを最大化します。たとえば、電圧計を使用すると、技術者はPLCの表面で実際の電圧を読み取ることができます。その後、PLCコード内の対応する値を知ることができます。
技術的なヒント: 多くのPLCプログラミング環境では、ファンクションブロックダイアグラム(FBD:Function Block Diagram)とユーザー定義関数ブロック(UDFB:User Defined Function Block)が使用できます。これらは、カプセル化以外は似ています。UDFBの各インスタンス化には独自のメモリ構造がありますが、FBDにはありません。例として、FBDを使用した場合、先に述べたset-and-remember機能を使用することができないことをご理解ください。
図3: FMAP UFDBはRung2に掛けられています。マッピングパラメータはドット記法を用いてRung1にロードされます。また、ADCの出力そのままの値はRung0にロードさ れます。
技術的なヒント: UDFBを実装する際は、細心の注意を払ってください。PLCの寿命は数十年単位であることを常に忘れないでくださ い。数年後、また、最初にプログラムを書いたプログラマーがいなくなって何年もたった後でも、技能者や技術者がトラブルシューティングを行い、コードを修正することになります。明確な文書化と一貫性のある目的は、UDFBに不可欠な要素です。別の言い方をすれば、分かりやすいプログラムを書くと言うことです。利口ぶってはなりません!
まとめ
ユーザー定義関数ブロック(UDFB)は、PLC の「C言語」の関数に相当します。どちらも、よく使用されるコードをカプセル化する方法を提供し、比較的読みやすい階層コード構造を提供します。これによって下位レベルが抽象化され、プログラマーや将来の産業技術者はプログラムの流れを簡単に追うことができます。どちらのソフトウェア構造も、プログラマーがライブラリから堅牢で完全にデバッグされたソフトウェアを選択できるため、時間を大幅に節約することができます。
ご健闘をお祈りします。
APDahlen
著者について
Aaron Dahlen 氏、LCDR USCG(退役)は、DigiKeyでアプリケーションエンジニアを務めています。彼は、技術者およびエンジニアとしての27年間の軍役を通じて構築されたユニークなエレクトロニクスおよびオートメーションのベースを持っており、これは12年間教壇に立ったことよってさらに強化されました(経験と知識の融合)。ミネソタ州立大学Mankato校でMSEEの学位を取得したDahlen氏は、ABET認定EEプログラムで教鞭をとり、EETプログラムのプログラムコーディネーターを務め、軍の電子技術者にコンポーネントレベルの修理を教えてきました。彼はミネソタ州北部の自宅に戻り、このような記事のリサーチや執筆を楽しんでいます。LinkedIn | Aaron Dahlen - Application Engineer - DigiKey