9

いくつかのレガシー ソフトウェア (RSLogix 500、聞かないでください) を使用して PLC をプログラミングしていますが、モジュラス演算をネイティブにサポートしていませんが、必要です。モジュラス、整数除算、ローカル変数、切り捨て操作 (ただし、丸めでハックできます) にはアクセスできません。さらに、利用可能なすべての変数は、データ型別に並べ替えられたテーブルに配置されています。最後に、たとえば12345.678 MOD 10000 = 2345.678.

式を作成すると、次のようになります。

dividend / divisor = integer quotient, remainder

2 つの明白な実装があります。

実装 1: 浮動小数点除算を実行しますdividend / divisor = decimal quotient。次に、切り捨て操作を一緒にハックして、integer quotient. それに を掛けて、divisorと の差を求めます。dividend結果は になりremainderます。

さまざまなタイプの変数がたくさん含まれているため、これは好きではありません。変数をサブルーチンに「渡す」ことができないため、複数の異なる変数テーブルにあるグローバル変数の一部を割り当てるだけで済み、従うのは困難です。残念なことに、「従うのが難しい」ことは重要です。なぜなら、保守作業員がいじるのに十分なほど単純である必要があるからです。

実装 2: while のようなループを作成しますdividend > divisor divisor = dividend - divisor。これは非常にクリーンですが、ループを使用しないという PLC プログラミングの大きなルールの 1 つに違反しています。誰かが不注意でインデックス カウンターを変更すると、無限ループに陥り、機械が狂ったり、回復不能な障害が発生したりする可能性があるからです。プラス ループは、メンテナンスのトラブルシューティングが困難です。さらに、ループ命令すらありません。ラベルとジャンプを使用する必要があります。うわー。

だから、誰かが巧妙な数学のハックや、これらのいずれよりも賢いモジュラスの実装を持っているかどうか疑問に思っています。+ - * /、指数、平方根、三角関数、対数、絶対値、および AND/OR/NOT/XOR にアクセスできます。

4

5 に答える 5

6

何ビットを扱っていますか?次のようなことができます。

if dividend > 32 * divisor  dividend -= 32 * divisor
if dividend > 16 * divisor  dividend -= 16 * divisor
if dividend > 8 * divisor  dividend -= 8 * divisor
if dividend > 4 * divisor  dividend -= 4 * divisor
if dividend > 2 * divisor  dividend -= 2 * divisor
if dividend > 1 * divisor  dividend -= 1 * divisor
quotient = dividend

にあるビットの数だけ展開しますdividend。これらの乗算のオーバーフローに注意してください。これは #2 と同じですが、n 反復の代わりに log(n) を使用するため、完全に展開することができます。

于 2013-02-12T22:22:27.087 に答える
5

物事を過度に複雑にし、コンピューター時間を無駄にすることを気にしない場合は、周期的な三角関数を使用してモジュラスを計算できます。

atan(tan(( 12345.678 -5000)*pi/10000))*10000/pi+5000   =   2345.678

真面目な話ですが、10000を1回または2回引く(「実装2」)方がよいでしょう。一般的な浮動小数点モジュラスの通常のアルゴリズムでは、ビットレベルの操作がいくつか必要になりますが、これはおそらく実行不可能です。たとえば、http://www.netlib.org/fdlibm/e_fmod.cを参照してください(アルゴリズムは単純ですが、特殊なケースのため、および64ビットがないと仮定してIEEE 754倍精度数値用に記述されているため、コードは複雑です。整数型)

于 2013-02-13T18:13:52.190 に答える
3

これはすべて完全に複雑すぎるようです。10000 でロールオーバーするエンコーダー インデックスと、特定のポイントで位置を追跡しているラインに沿って転がるオブジェクトがあります。ラインに沿ってプロジェクトの停止ポイントまたはアクション ポイントを進める必要がある場合は、必要な数のインチを追加し、目標結果が 10000 より大きい場合はすぐに 10000 を減算します。

あるいは、または加えて、PLC スキャンごとに常に新しいエンコーダ値を取得します。現在の値と最後の値の差が負の場合は、作業中の連絡先に通電してラップ イベントにフラグを立て、そのスキャンの計算に適切な修正を加えることができます。(**または、以下のようにセカンダリ カウンターをインクリメントします)

実際の問題について詳しく知らなければ、より具体的な解決策を提案することは困難ですが、より良い解決策があることは確かです。ここではMODの必要性はまったくありません。さらに、フロアの担当者は、難読化されたウィザードのものでマシンをいっぱいにしないことに感謝します.

私は引用します:

最後に、12345.678 MOD 10000 = 2345.678 のように、浮動小数点数に対して機能する必要があります。

これを行うために存在する素晴らしい関数があります - それは減算です。なぜそれよりも複雑にする必要があるのですか?コンベア ラインが実際に 833 フィートよりも長い場合は、必要な地面をカバーするのに十分な距離が得られるまで、プライマリ インデックスのロールオーバーで増加する 2 番目のカウンターをロールします。

たとえば、100000 インチのコンベア メモリが必要な場合は、10 でロールオーバーするセカンダリ カウンターを使用できます。プライマリ エンコーダーのロールオーバーは上記のように簡単に検出でき、毎回セカンダリ カウンターをインクリメントします。したがって、作業中のエンコーダーの位置は、カウンター値の 10000 倍に現在のエンコーダー値を加えたものになります。拡張ユニットのみで作業し、パーツを失わないように必要な値でセカンダリ カウンターをロールオーバーさせます。この場合も、問題は単純な減算になります (上記のように)。

たとえば、遊星歯車の回転部品ホルダーでこの手法を使用します。一次回転ごとに 1 回ロールオーバーするエンコーダーがありますが、遊星歯車衛星部品 (それ自体がステーター ギアの周りを回転します) は、同じ開始方向に戻るために 43 の一次回転を必要とします。プライマリ エンコーダーのロールオーバー ポイントでインクリメント (または方向に応じてデクリメント) する単純なカウンターを使用すると、部品の位置を完全に絶対的に測定できます。この場合、セカンダリ カウンタは 43 でロールオーバーします。

これはリニア コンベヤでも同じように機能しますが、唯一の違いは、リニア コンベヤが無限の距離を移動できることです。その場合、問題は、ライン上の最悪のケースの部分がたどる最長の線形経路によって制限されるだけで済みます。

RSLogix を使用したことがないことに注意してください。一般的な考え方は次のとおりです (ここでは一般的なシンボルを使用しました。構文はおそらく少し間違っていますが、理解できるはずです)。

RSLogix サンプル

ENC_EXT上記により、エンコーダーを 10k インチから 100k インチに実質的に変換した値になります。コンベアが逆に実行できるかどうかはわかりません。可能であれば、ダウンカウントも処理する必要があります。プログラムの残りの部分全体がENC_EXT値でのみ機能する場合は、エンコーダーが 10k しかないという事実について心配する必要さえありません。これは 100k (または任意のもの) になり、ラップアラウンドはモジュラスの代わりに減算で処理できます。

あとがき:

PLC は何よりもまずステート マシンです。PLC プログラムの最善の解決策は、通常、この考えに調和するものです。ハードウェアがマシンの状態を完全に表現するのに十分でない場合、PLC プログラムは、不足している状態情報のギャップを、持っている情報で埋めるために最善を尽くす必要があります。上記のソリューションはこれを行います - 不十分な 10000 インチの状態情報を取得し、プロセスの要件に合わせて拡張します。

このアプローチの利点は、コンベヤだけでなく、ライン上のすべての部品についても、絶対的な状態情報を保存できることです。トラブルシューティングとデバッグのためにそれらを前後に追跡でき、将来の拡張のために使用するはるかに単純で明確な座標系を使用できます。モジュラス計算では、状態情報を破棄し、機能的な方法で個々の問題を解決しようとします。これは多くの場合、PLC で作業するための最良の方法ではありません。他のプログラミング言語から知っていることを忘れて、別の方法で作業する必要があります。PLC は別の獣であり、そのように扱われると最適に機能します。

于 2013-02-13T11:43:24.160 に答える
3

サブルーチンを使用して、話していることを正確に行うことができます。トリッキーなコードを隠すことができるので、保守技術者が決して遭遇することはありません。これは、ほぼ間違いなく、あなたと保守担当者にとって最も理解しやすいものです。

RSLogix500 を使用してからしばらく経っているため、いくつかの用語が間違っている可能性がありますが、要点は理解できます。

浮動小数点と整数のデータ ファイルをそれぞれ定義し、MOD_F と MOD_N の行に沿ってシンボルを指定します。これらを十分に威圧的にすると、メンテナンス技術者はそれらを放っておき、数学中にパラメーターとワークスペースを渡すだけで済みます。

それらがデータテーブルを台無しにすることを本当に心配しているなら、それらを保護する方法がありますが、私はそれらが SLC/500 にあることを忘れてしまいました.

次に、可能であれば、現在使用されているものから数値的に遠く離れたサブルーチンを定義します。MODULUS のような名前を付けます。繰り返しになりますが、保守担当者は、プログラミング名のように聞こえる場合、ほとんどの場合 SBR を避けます。

JSR 命令の直前のラングで、処理する変数を MOD_N および MOD_F データ ファイルにロードします。これらのラングに、MODULUS SBR のデータをロードするように指示するコメントを付けます。コメントは、プログラミングのバックグラウンドを持つ人にとってわかりやすいものにしてください。

必要な場合にのみ、JSR を条件付きで呼び出します。保守技術者は、実行されていないロジックのトラブルシューティングに煩わ​​されることはないため、JSR がアクティブでない場合、めったに調べません。

これで、自分の小さな壁に囲まれた庭ができました。そこでは、メンテナンスに関与することなくループを書くことができます。これらのデータ ファイルのみを使用し、それらのファイル以外の状態が期待どおりであると想定しないでください。つまり、間接アドレス指定は信頼できません。MODULUS JSR 内でインデックスを定義する限り、インデックス付きアドレッシングは問題ありません。着信インデックスを信頼しないでください。MOD_N ファイルからの 1 つの単語、ジャンプ、およびラベルを使用して FOR ループを作成するのは非常に簡単です。実装 #2 全体は 10 ラング程度未満である必要があります。式命令か何かを使用することを検討します...式を入力するだけです。その命令には 504 または 505 が必要な場合があります。浮動小数点演算と整数演算の組み合わせに適しています。ただし、結果をチェックして、丸めによって問題が発生しないことを確認してください。

完了したら、可能であれば完全にコードを検証します。このコードが数学のオーバーフローを引き起こし、プロセッサに障害が発生した場合、その終わりを聞くことはありません。シミュレーターがある場合はシミュレーターで実行し、奇妙な値を使用して (関数入力のロードを何らかの形で台無しにする場合に備えて)、PLC に障害がないことを確認します。

これらすべてを実行すれば、PLC で通常のプログラミング手法を使用したことに誰も気付かず、問題ありません。それが機能する限り。

于 2013-02-16T20:45:34.557 に答える
0

これは @Keith Randall の回答に基づくループですが、減算による除算の結果も維持されます。明確にするためにprintfを保持しました。

#include <stdio.h>
#include <limits.h>
#define NBIT (CHAR_BIT * sizeof (unsigned int))

unsigned modulo(unsigned dividend, unsigned divisor)
{
unsigned quotient, bit;

printf("%u / %u:", dividend, divisor);

for (bit = NBIT, quotient=0; bit-- && dividend >= divisor; ) {
        if (dividend < (1ul << bit) * divisor) continue;
        dividend -= (1ul << bit) * divisor;
        quotient += (1ul << bit);
        }
printf("%u, %u\n", quotient, dividend);
return dividend; // the remainder *is* the modulo
}

int main(void)
{
modulo( 13,5);
modulo( 33,11);
return 0;
}
于 2013-02-13T12:21:25.253 に答える