08-05-2010 05:07 AM
現在LabVIEWを使って高速制御を行いたいと考えております。
しかし、以前調べた際に1ループでの時間制御は1msecが限界との記述がありました。
LabVIEWで1msecより早い時間の正確な時間制御をできないのでしょうか?
なにか、方法がありましたら、お願いします。
また、もし時間の指定を外した状態でループを回すと
もっとも軽い演算(一回の足し算等)で何secのスピードで処理するのでしょうか?
時間を表示させていると、1msec以下で処理しているように見えるので。
LabVIEWは8.6を使用しています。
初心者でもうしわけありませんが、
よろしくお願いします。
解決済! 解決策の投稿を見る。
08-06-2010 10:47 AM
「1ループでの時間制御は1msecが限界」というよりも、「LabVIEWで計測可能な時間単位が1msec」
ということなので、1回の足し算とか、軽いループの回る時間などは、1msecよりはるかに短いです。
私のマシンの場合ですが、空っぽのForループの1回分の時間は、10~11nsec(ナノ秒)くらいでした。
ただし、明示した時間単位で制御しようとすると、「次のミリ秒まで待機(Wait Until Next Millisec)」関数などを
使うことになるので、どうしてもその最小単位の1msec単位での処理になってしまいます。計測する時間差も
1msecなので、みかけのばらつきも(量子化誤差)も1msec単位で生じてしまいます。
(つまり、ほんとは一定速度だとしても、20msec、21msec、20msec、・・・と1msec単位でばらついて見えたり)
さて、1msec以下でループの回る速度を制御する方法ですが、APIのQueryPerformanceCounter()と
QueryPerformanceFrequency()を使うと、一応、最小制御時間単位として1μsec(マイクロ秒)以下にすることもできます。
添付に4つのサンプルと、上記の基本APIを含むllb、およびいくつかのdllを付けました。ただし、ver.6です。
100806-sample1-(Comparison).vi
LabVIEWの(1)Wait Until Next Millisec関数、(2)Wait関数、および(3)APIのQueryPerformanceCounter()を
使った1秒単位のループの比較をしています。ジッタが少ないのは(3)ですが、ほかに走っているプロセスの
負荷にも依存するのでご注意ください。
100806-sample2-(LoopExecVariance).vi
10000回のForループの個々のループの時間差をグラフに表し、ヒストグラムも表示します。
他のプロセスの負荷の影響を受けるなどで、10000回のうちに回る速度が変化しているようです。
100806-sample3-(EmptyLoopExec).vi
空っぽのForを100万回まわし、その前後の時間差から空のループ1回分の平均時間を計算します。
LabVIEWの関数ではミリ秒単位なので、あまり精度よく測れませんが、API関数を使うと
数1000~10000倍以上の精度で計測可能です。
(実行すると値がパラパラ変わるのは、「100万回のループ」をさらに何度も繰り返しているため)
100806-sample4-(0.25msecLoop).vi
0.25msec(250μsec)単位でループを回しています。実際の時間差も表示しているので、0.25msec単位で
回っていることがわかると思います。
数値上は、1usecとかも指定できますが、ループ内の処理の合計がそれを上回れば、当然1usecでは回せません。
実際、カウント値を読み出すオーバーヘッドだけでsample2のように数10μ秒はかかるようなので、
100usecくらいが安定して回せる限界かも知れません。
QueryPerformance.llb
APIのQueryPerformanceCounter()とQueryPerformanceFrequency()を呼んでいるllbです。
なお、これらのAPI関数にはクセがあるらしく、オーバークロックや、動作中に動的にクロックを変化させるようなものでは、
きちんと測れないこともあるらしいです。そういう場合は、別途LabVIEWのタイマ関数などで「1秒」のカウント値を
数えておき、自分で校正する、という手もあります。
SmallTick.dll
マイクロ秒単位のWait(Wait Until Next Usec)を提供する簡易的な自作のdllです。
sample1とsample4で使っています。
なお、これらの呼び出しに「U32が2個」のクラスタをつないでいますが、これは、LabVIEW7.1以下では
64ビット整数が使えないためで、LabVIEW8以上では、64ビット整数が使えると思いますので
Call Library Functionにつながっているクラスタは、すべて64ビット整数をつなげばOKと思います。
Int64.dll
64ビット整数を演算するための簡易的な自作dllです。LabVIEW8以上は、普通に64ビット整数で演算して下さい。
(同時にたくさん添付できないようなので、いくつかに分割して投稿します)
ただ、基本的に実行速度はWindows起因でいくらでも変わってしまうので、たとえば0.25msecでループを
回すように書いた場合、処理が軽ければだいたいば0.25msecで回ってくれますが、他のプロセスが重くなると
すぐ遅延が生じますので(これは普通に待機関数を使った場合も同じ)、その点はもともと保証されていないことを
ご了承ください。上記はあくまで1msec以下の計測と制御を試みた例ということで。
08-06-2010 10:49 AM - 編集済み 08-06-2010 10:51 AM
添付の追加分です。
08-06-2010 10:56 AM
・・・・よく考えたら、初めから7つのファイルを1つのzipにして添付すれば良かったですね。。。
(蛇足ながら)
08-09-2010 05:54 AM
M.Shiraishi 様
ナイス突込み。
いつも勉強になります。有難うございます。
08-09-2010 06:35 AM
M.Shiraishi 様
助かりました。
ありがとうございます。
こんなAPIがあったのですね。
勉強になりました。
また、機会がありましたら御教授ねがいます。
ありがとうございました。
08-09-2010 09:21 AM
xyzz様、ニックS様
どうもありがとうございます。
なお、SmallTick.dllですが、一応、どう実装したかソースを書いておきます。たいしたものではございませんが・・・
SmallTick.cpp
#include <stdio.h>
#include <windows.h>
__declspec(dllexport) void _stdcall WaitUntilNextUsec(unsigned long Usec,LARGE_INTEGER *Tick);
__declspec(dllexport) double _stdcall PerformanceFrequencyInfo(LARGE_INTEGER *Frequency);
void _stdcall WaitUntilNextUsec(unsigned long Usec,LARGE_INTEGER *Tick)
{
long long Count,Quot;
LARGE_INTEGER Frequency;
QueryPerformanceFrequency(&Frequency);
Count=(*(long long *)(&Frequency))*Usec/1000000; // Frequency*Usec/1000000 指定時間内のカウント
QueryPerformanceCounter(Tick);
Quot=(*(long long *)Tick)/Count;
do {
QueryPerformanceCounter(Tick);
} while((*(long long *)Tick)/Count==Quot);
return;
}
double _stdcall PerformanceFrequencyInfo(LARGE_INTEGER *Frequency)
{
QueryPerformanceFrequency(Frequency);
return((*(long long *)Frequency)*((double)1.0)); // [Hz]
}
WaitUntilNextUsec() は、第1引数でusec単位の待機時間を入力し、第2引数に待機完了時のカウント値(64bit)を返します。
(入力が待機時間、出力が待機後のTick値なのは、標準の Wait や WaitUntilNextMillisec と同様のつくり)
中身の do ... while 文は、CPUに負荷をかけるかも知れません。
実際、この関数を繰り返し呼ぶと、私のPCはファンの音が大きくなるような・・・使いすぎてPCが壊れてもいけないので念のため。
(うまく負荷を回避しつつ、精度を維持できる書き方があれば知りたい)
関数内のローカル変数 Frequency は、引数の *Tick の領域で代用可能で、わずかにメモリ節約します。
PerformanceFrequencyInfo() のほうは、単に QueryPerformanceFrequency() が返す64bitの「1秒あたりカウント数」を、
64bit整数と double の両方で返します。[Hz]単位の数値で簡便に扱いたい場合に使います。
double は64bit整数のすべては扱えませんが(bit数が同じなので)、QueryPerformanceFrequency() が返す値は
そんなに大きくないので(まず32bitに収まっているので)、doubleで返して、以降の扱いがしやすいようにしています。
以上、ご参考下さい。
08-09-2010 04:38 PM
本当に詳しくありがとうございます。
ぜひ参考にします。
ちょうど最近winAPIをかじっり初めたところなので
ちょうど上記に非常に興味をもっておりました。
ソースコードまで教えていただき
しっかり勉強させていただきます。
ありがとうございます。
08-09-2010 04:38 PM - 編集済み 08-09-2010 04:41 PM
重複投稿のため削除しました。