【LabVIEWでDAQを扱う⑧】複数のタスクやデバイスを扱う

LabVIEWでDAQを扱う

スポンサーリンク

LabVIEWでデータ測定用のハードウェア(DAQ)を扱おうという方に向けて、道しるべになるような情報を発信していこうという試みです。

シリーズ8回目として複数のタスクを扱うプログラムの紹介です。

この記事は、以下のような方に向けて書いています。

  • タスクの数に制限はあるの?
  • アナログの入出力を一つプログラムで行いたい
  • アナログとデジタルのタスクが一つのプログラムに混在していてもいいの?

もし上記のことに興味があるよ、という方には参考にして頂けるかもしれません。

なお、前回の記事はこちらです。

スポンサーリンク

タスク数には制限がある

これまでの記事で、アナログ入出力とデジタル入出力それぞれの使い方について紹介しました。この記事では以降、これらの「アナログ入力」、「アナログ出力」などの区分をタイプと呼ぶことにします。

どのタイプであっても、タスクというものを構成する必要があり、チャンネル名を指定して必要に応じてタイミングやトリガの設定をしてそのタスクを開始、停止させてハードウェアを動かす、ということは変わりません。

今までの話では、一つのviにあるタスクは一つだけでした。ですが、実際は同じviに複数のタスクを用意することができます。

ただこういったタスク、プログラム上で好き勝手に作れるかというとそうではなく、制限があります。ハードウェアを動作させるうえでこの制限を守らないとエラーが起きてしまいます。

とても大雑把に言うと、使用するハードウェアごとに制限があります。例えば「一つのアナログ出力デバイスに対して二つのハードウェアタイミングタスクを作ることができない」、といった具合です。

具体的には、NI 9263というアナログ出力デバイスに対して二つのハードウェアタイミングのタスクを定義するとエラーが起こります。NI 9263はチャンネルを4つもっていて、このうちの二つのチャンネルに別々のハードウェアタイミングタスクを割り当てようとしてもできない、ということです。

これは一つのプログラムでそうしているかどうかは問いません(二つのプログラムに分けていたとしても同じくエラーが起こります)。

シミュレーションデバイスでもエラーが確認できるのてテストした結果が以下の図です。

ただし、ソフトウェアタイミングであればエラーは起こりません。

同じ型番(NI 9263)であっても、物理的に別個体のアナログ出力デバイスでかつ別のシャーシに入っているものであれば、一つのプログラムでそれぞれにハードウェアタイミングタスクを作ることができます。(もちろんプログラム、viを分けても大丈夫です。)

結構ややこしいかもしれませんが、これらの制限の詳しい内容についてはNI-DAQmxヘルプ(デフォルトだとC:\Users\Public\Documents\National Instruments\NI-DAQ\Documentationに入っています)の目次から「NI-DAQmxデバイスに関する注意事項」>>「同時タスク」の項目で特に「CompactDAQおよび~」の項目で確認できます。

上記のアナログ出力とは違う例として、アナログ入力デバイスについても紹介します。

ヘルプの内容を見ると、CompactDAQシャーシを使用してアナログ入力のデバイスを複数スロットに入れた場合、そのシャーシではハードウェアタイミングで測定するアナログ入力のタスクは3つまで作ることができるということになります。

そのシャーシに入っているアナログ入力のデバイス単体に対しては1つしかタスクを作れません(こちらもヘルプに記載があります)が、全体で3つ作れるというのは 他のタイプ(アナログ出力やデジタル入出力など)については1つしか作れないのと比べるとアナログ入力だけ特殊ですね。

「え、じゃあ例えば同じシャーシに4つ以上のアナログ入力のデバイスを入れても使えないの?」というとそんなことはなく、方法はあります。

制限があること、これに気を付けてプログラムを組む必要があることを踏まえて、改めてどうこれらの制限の中でプログラムを作るかを以下で見ていきます。

同じタイプのタスクを扱う

まずは同じタイプのタスクを扱う場合に考えることを紹介していきます。

同じCompactDAQシャーシに例えば4つのアナログ入力のデバイス(NI 9215など。ただしこれら4つは同じ型番である必要はないです)が入っている場合、これらをどうやって扱っていくか。

単純な方法は、一つのタスクに複数のデバイスのチャンネルを含めるという方法です。

これは特別に新しく考える必要はなく、DAQmxチャンネルを生成の関数を一つのタスクで複数使用する、あるいはフロントパネル上の物理チャンネルを指定する制御器で複数のデバイスのチャンネルを指定するだけです。

例えば、以下の図は同じCompactDAQシャーシに入っているNI 9215二つを一つのタスクに入れた場合の例です。DAQmx仮想チャンネルを作成の関数で最大値最小値などのパラメタを設定できますが、この方法を使用するとデバイス毎に設定を変えられます。

一つのタスクにアナログ入力、アナログ出力などをごちゃ混ぜにはできません。が、一番最初に紹介したアナログ出力の例で一つのシャーシに二つのアナログ出力デバイスがある場合でも、この方法ならタスクは1つだけなのでプログラムを動かすことができます。

またこのDAQmx仮想チャンネルを作成の関数を数珠つなぎにするプログラムを作る方法では、アナログ入力での測定の種類が異なっていても使えます。

例えば、あるデバイスでは電圧測定をして別のデバイスでは温度測定をして、といった測定の種類が異なる場合も同じタスクに入れ込むことができます。

このように物理チャンネルを数珠繋ぎのようにつなげる形以外にも、以下のようにDAQmxチャンネルを生成は一つで、物理チャンネル選択時にまとめてデバイスを選択するといった方法もあります(これはチャンネル拡張という呼び名があるようです)。

物理チャンネル選択時にまとめてデバイスを選べて便利な反面、これらの測定の最大値、最小値は全て同じ設定値となります。

DAQmx仮想チャンネルを生成を数珠つなぎにする場合もチャンネル拡張の場合も、同じタスクではどのデバイスも一つのタイミング、トリガ設定を共有します。一つのタスクにつきタイミングの設定やトリガの設定は一つ、ということです。

この場合、弊害が生まれてくる可能性があります。例えば「あるデバイスでは1 kHzでサンプリングレートを指定したいのに別のデバイスでは10 kHzとしたい」といったようにサンプリングレートの条件が異なる場合です。

この場合への対処方法としては、

  • (タスクの制限に引っかからないのであれば)タスクを分ける
  • 早いサンプリングレートの方に設定してソフトウェア上で間引きする

といった回避策をとります。

タスクを分ける

元も子もないと思うかもしれませんが、異なる設定をさせたいということであればタスクを分けるということが選択肢となります。またこの方法はCompactDAQシャーシを使用している場合には複数タスクが許されるアナログ入力が前提になります。

単にタスクを増やすだけなのですが、タスクが分かれる分、それぞれのタスクで構成した測定は(そのままでは)同じタイミングではなくなります。いわゆる同期した状態ではなくなるということです。

たとえ二つのタスクが同じサンプリングレートだとしても、それらのサンプリングのタイミングを決めているサンプリングクロックは別々のものを使用するためです。

実機で試した一例が以下の図です。以下では、測定する信号は全く同じもの(同じ信号源からの信号を分割している)でありどちらも同じサンプリングレートですが、測定された結果は微妙に異なっています。

この結果の図を見て違和感がある方がいるかもしれません。全く同じ信号源の信号を測定していて値が横にずれるのは不自然なんじゃないか、と。

上の図は、「タイムスタンプを無視」した状態の結果になっています。なので横軸は実時間ではなく、データの順番を表わしているだけです。実時間にすると、データの取得タイミングがずれている様子は以下のように見えます。

二つのタスクで測定のタイミングを合わせたい、同期させたいという場合には、それらのタスクで「同タイミングにしてください」という設定を明示的に行う必要があります。

この同期のための手法はかなり奥が深く、使用するモジュールによって共有するべきクロックや組み方が変わるので、ここではそこまで深く扱いません。ただ、ベースになる考え方として「クロックを共有する」ということを意識することで組める場合も多いです。

単純な例は、サンプリングクロックの共有で、例えば以下のようなプログラムのようにします。

「予約」と書かれた列挙体が入っている関数はDAQmxタスクを制御関数です。一行目のタスクが先に実行されることで「このタスクはaiのsampleclockを使用します」という「予約」をしてから、二行目のタスクが実行され、「このタスクではaiのsampleclockを参考にします」という設定をDAQmxタイミング関数で行っています。

二行目のタスクが始まる前に一行目のタスクが始まると、aiのsampleclockを参照するのに後れを取ることになるため、必ず二行目(サンプリングクロックを参照する側)のタスクを開始させてから一行目(サンプリングクロックを参照される側)のタスクを開始します。

予約をしない場合、一行目のタスクが、aiのsampoleclockを使用しない可能性があります。アナログ入力は3つまでタスクを作れるという話でしたが、ai以外にもte0、te1という名前のsampleclockを持ち、これらのうちどれが使用されるかはタスクを構成した順番で決まります。二行目のタスクを設定し始める前に「予約」することで、一行目のタスクが確実にaiのsampleclockを使用するようにするための設定という意図でこのようにしました。

こちらの結果も実時間で表すと以下のようになっています。タイムスタンプで表しても二つのタスクの測定結果はタイミング的に一致しています。

なお、場合によっては開始のタイミングを揃えたいという場合もあるかもしれません。そんなときにはDAQmxタイミングの関数ではなく、DAQmxトリガ関数を使用して例えば以下のように書けます。

このとき指定しているのはaiのStartTriggerです。図でcDAQchassis/ai/StartTriggerとなっていますがこのcDAQchassisというのは両タスクで使用されているデバイスが入っているCompactDAQシャーシの名称です。

一行目のタスクでは開始トリガを指定していませんが、DAQmxトリガの関数が使用されていないタスクはDAQmxタスクを開始関数が実行されるときに暗に開始トリガが設定されていると思ってください。

測定のタイミングを合わせるのであればダイレクトにサンプルクロックを共有する方法がいいですが、開始のタイミングさえあっていればいいということであれば開始トリガを合わせる上記の方法で事足ります。

なんだか話がそれてしまいましたが、タスクを分ける際に参考にしてみてください。

ソフトウェア上で間引きする

どちらかというとソフトウェア上で間引きする方法の方が楽です。一つのタスクに複数のチャンネルをまとめている場合にこの方法を使用できます。

データの間引きの仕方についてはデシメート1D配列を使用したりForループと商&余りの関数を駆使した方法などが考えられます。

デシメートの関数は、元々の配列をデシメートの関数の長さ分だけ分割するといった働きをする関数です。

この性質を利用して、サンプリングレートを実質的に「遅く」できます。例えば1000 Hzでとったデータ配列があるとして、この配列の要素を1つおきに抜き出すと、それは元々の測定を500 Hzで行っているのと同じ結果となります。(データの数も半分になります)

例えばNI 9205とNI 9206という二つのアナログ入力デバイスに対してNI9205のサンプリングレートを1000 Hz、NI 9206のサンプリングレートを実質的にその4分の1にしたい場合には以下のような組み方が考えられます。

ただ、デシメートの関数は長さを固定するので、柔軟性がありません。個人的には間引き間隔を自由に調整しやすいという意味では商&余りの関数を使用する方が便利だと思っています。

この方法はForループの性質をフルに利用しています。以下に簡単に例を示しています。

こちらの例で、Forループの反復端子(「i」と書かれた部分)はForループのループ回数が出力され、最初は0なので、0÷3、この余りは0であり「0に等しい?」の関数はTRUEとなります。次にiからは1が出ますが、1÷3の余りは1であり、「0に等しい?」はFALSEとなります。これを繰り返すと、「0に等しい?」がTRUEになるのはiが0、3、6、9、・・・と3の倍数ごとになります。

「0に等しい?」がTRUEになったときだけこの内側のForループを抜けるように出力トンネルに「条件」の設定をしている(出力トンネルを右クリックで「条件」)ことで、簡単に3分の1にできます。

以下の図がDAQのプログラムへの実装例です。商&余りの関数に入力している定数は4となっていますが、この数値を変えれば「N分の1」のNを容易に変更できます。

異なるタイプのタスクを扱う

次に、異なるタイプのタスク、例えばアナログ入力とアナログ出力のタスクを扱うことを考えます。

が、単純にこれらのタスクをしたいだけなのであればやることは特に難しくはなく、同じviにそれらのタスクをそのまま使用するだけです。

タスクの制限にさえ引っかからなければ良いので、アナログとデジタルをごちゃ混ぜにしたプログラムも可能です。

しかしこの場合も、プログラム上でそのまま二つのタスクを置いただけではそれぞれで同期しているわけではありません。

入力も出力もそれぞれ独立したタイミングで構わないんだ、ということであれば問題ないのですが、入力と出力のタイミングを合わせたい、ということであれば同期させる必要があります。

これについてもデバイスの種類によって例外があるようで、奥が深い内容です。

ただ、癖のあるデバイスでない場合には、既に紹介していたような方法と同じように「片方のタスクのサンプリングクロックをもう片方のタスクのサンプリングクロックとして指定する」という考え方で動作をさせられます。

とても単純な例ですが、イメージとしては以下のような形になります。

これはアナログ、デジタルの垣根を超えた場合でも同じような考え方で組める場合があります。

同期についての様々なパターンはNational Instruments社の出している同期についての資料でいくつかの組み合わせでのパターンが紹介されているので、こちらを参考にすると組むべきプログラムがそのまま見つかるかもしれません。(National Instruments社自体はアメリカの会社なので、日本語よりも英語で探すと資料が見つけやすい場合が多いです。中身読むの大変ですけれどね日本語で資料ありました)

LabVIEWでNI-DAQmxを使用してアナログ入力Cシリーズモジュールを同期させる
CompactDAQコントローラおよびシャーシは、加速度、温度、歪みなどといった様々なタイプの計測に適した種類豊富なCシリーズI/Oモジュールに対応しています。

アナログやデジタルといった異なるタイプのタスクを紹介し、それらを一つのプログラムで組み合わせて使用する場合についても紹介してきました。タスク間でのタイミング合わせのための同期の仕組みについてもちらっと触れました。参考にしてもらえたら嬉しいです。

ここまで読んでいただきありがとうございました。

コメント

  1. 初心者 より:

    大変参考になります。
    初心者で申し訳ありませんが質問があります。

    visaを使用したシリアルデータ(GPSのnmea 10Hz)とdaqデータ(analog入力 2000Hz)を同期させたいと考えています。

    ・シリアルデータはdaqで扱うことはできますでしょうか?
    ・現在同期の手段がわからないため、「二つのタスクを置いただけ」の入力も出力もそれぞれ独立した状態で記録を行っています。最初の5分くらいは時間のズレがないのですが、徐々にシリアルの記録がおくれていってしまい遅延が発生します。この原因はなにか分かりますでしょうか?

    • コメントいただきありがとうございます。

      これは私個人の思うことなので正解かどうかは保証できないのですが、「シリアルデータはdaqで扱うことはできるか」については、できない、だと思います。
      GPSのNMEAというもの、恥ずかしながら知らなかったので調べてみたのですが、「センテンスの集まり」と書いてあったので、いわば文字データということになるかなと思います。
      DAQはタスクで扱えるのが「アナログ入出力」、「デジタル入出力」、「カウンタ入出力」に限られるので、もし何らかの方法で「GPSのNMEA」をこれらのいずれかに変換するような仕組みが
      あればできるのかもしれないですが、多分そのような変換ってないですよね・・・

      同期については、シリアル通信のような「通信」系はあまりタイミングの制御ができない印象です。本来何か複数の装置のタイミング(信号の入力とか)を同期して合わせるには、
      それら同期させたい対象すべてに、基準となる信号(クロックと呼ばれることが多いと思います)をいれてあげて、その信号を基準に各装置が動作するようにすることが必要になります。
      お使いのデバイスでそういったことができるのかは調べる必要があると思いますが、私個人で経験がないので何とも言えないです。すいません。

      ただ、ソフトウェア的に遅延を起きにくくすることはできるかもしれないです。例えばもしプログラム的に二つのタスクを置いて二つのループを使用しているような場合には、「次のミリ秒倍数まで待機」の関数で
      タイミングを制御することで解消できるかもです(ズレの原因にも依りますが)。

      文章だけだとイメージ付けづらいと思うのですが、プログラムの形としてはざっくり、下の資料の1ページ目下から4番目の投稿にある図みたいな状態のことだと考えてください。
      図だと「待機」の関数を使用しているのの代わりに「次のミリ秒倍数まで待機」を使う、という感じです。
      https://forums.ni.com/t5/LabVIEW/Synchronize-Serial-Communication-and-DAQ/td-p/3670645

      待機関数と次のミリ秒倍数まで待機という、二つのループのタイミングを制御する関数は使う目的が明確に違うので区別する必要がありますが、区別がわからないよ、という場合には
      もしよかったら以下の記事を参考にしてもらえるかもしれないです。
      https://marblerule.com/loop_timing/

      ただ、これはあくまでループにタイミング制御の関数を使用している場合の方法になります。現在daqデータとして2000Hzのアナログ入力をされているということは、
      タイミング制御の関数は使っていないかもしれないですね。
      DAQmx読み取りで読み取るサンプル数を「2000」とすればループ1回に1秒かかることになるので、敢えて次のミリ秒倍数まで待機の関数をループに入れたとしてこの関数に「1000」を
      指定すればいいのかなと思います。そして、シリアル通信を行っている方のループにも同じく次のミリ秒倍数まで待機に1000を指定して試してみてください。

      すいません、長文で書いた割に全然効果がない話かもしれないですが、参考になればと思います。

  2. 初心者 より:

    早速のご返答ありがとうございます。
    文字列だとdaqはダメそうですね。
    ミリ秒待機は存じあげなかったので勉強して試してみたいと思います。

    サイトの「まずこれシリーズ」で勉強させていただいています。非常にわかりやすく助かっています。現在も頻繁に更新されているようなので今後も参考にさせていただきます、ありがとうございました。

タイトルとURLをコピーしました