今回は、超音波センサと焦電型赤外線(PIR)センサを使ってみます。
白いドーム状のレンズが付いた、以下の様なモジュールです。
レンズは接着剤などで固定されてはおらず、カパッと外すことができます(上写真右側)。すると、その下に型番と思われるシルクが見つかりました。
型番(DSN-FIR800)からデータシートを検索する(別ウインドウが開きます)
どうやら、熱源が横切ったことを検知して、信号を出力してくるモジュールの様です。上の写真(レンズを外した方)を良く見ると、丸いセンサにポチっとマークが見えています。この方向が横軸(横切る方向)です。たぶん、レンズを外して使うと、横方向の移動のみ検知可能なのだと思われます。
白いドーム状のレンズは、光(赤外線)をインタリーブすることで、色々な方向の動きを検知できるようにするために被せているのだそうです。
モジュールから出ている端子は3本、VCC、GND、OUTです。熱源の移動を感知すると、OUT端子にHIGHパルスが出るようです。半固定抵抗が2つ付いていて、パルスの幅と感度を調整できるようになっているので、テストプログラム(test_pir_usnc.ino)では、OUT=HIGHの間だけLED(UNOのオンボード)を光らせるようにしてあります。
焦電型赤外線(PIR)センサモジュールの電源(VCC)は3〜5[V]ですので、Arduinoから5Vを供給し、GNDも接続します(モジュール基板にレギュレータが載っていて、必要な電圧を生成しているようです)。OUTはD2に接続しておいてください。
※Logiscope(【ina】コマンド)でOUTの電圧を測定したところ、3.25Vでした。恐らく、OUTを出力しているICは3.3V定格ですね。まあ、5VのArduinoでも、受けることはできますが……、ちょっと気持ち悪い。
評価プログラム:test_pir_usnc.ino
Arduino | 接続 | 焦電型赤外線(PIR)センサ |
5V | → | VCC |
GND | ⇔ | GND |
D2 | ← | OUT |
熱源の動きを検知すると、Arduino UNOのオンボードLEDが光りますので、手をかざして動かして見てください。もし、半固定抵抗の初期値が変な設定になっていると、うまく光らない可能性もあるので、一番最初に実験する時は要注意です。
夜、人が通るとパッと明かりがつくタイプの照明などに使われていますよね。点いた明かりを消すまでの時間をパルス時間で設定できるように、ずいぶんと長いパルス時間を設定できるみたいです。
※これなら、焦電型赤外線(PIR)センサと「#3-8 ブラシDCモータ」で使ったリレーを組み合わせるだけで、一定時間照明を点灯する、程度の応用ならArduinoなしで実現可能です。
実験では、そんなに長い間LEDが光り続けていると使い難いので、パルス時間の方は反時計回りいっぱいに回しておくことをお勧めします。それでも、一度検知すると2.5sはLEDが光り続けるので、消灯するまでしばらく待つことになります。
なお、筆者手元の焦電型赤外線(PIR)センサモジュールは、「リトリガモード」に設定されていて、一度熱源の動きを検知してOUT=HIGHになっている間に、もう一度動きを検知すると、OUT=HIGHの時間が延長されるようになっています。別なモード(「非リトリガモード」)に変更するには、はんだ付けするタイプのジャンパをショートしてください(筆者手元のモジュール)。……多くのアプリケーションで、「リトリガモード」の方が使い勝手が良いので、特にモード変更する必要は無いと思います。
続いて超音波センサで距離を測定してみます。
基板のシルクに型番が見えます。いつもの様にデータシートを検索してみましょう。
型番(HC-SR04)からデータシートを検索する(別ウインドウが開きます)
「動作周波数40kHz」とあるので、40kHzの超音波を使っている、と言うことでしょうか?人間の可聴域が20〜20k[Hz]と言われていますので、耳で聞こえる一番高い音(〜20kHz)の倍の周波数、と言うことですね。
端子はVcc、Gnd、Trig、Echoの4本。データシートによると、Vcc=5V (消費電流15mA)ですので、GndとVccをArduinoに接続します。
Trig端子に10μs以上のHIGHパルスを与えると、超音波を発信して距離を測定してくれるようです。測定結果はEcho端子のHIGHパルス幅から計算します。
Echo端子のHIGHパルス幅をE[μs]とすると、データシートより、以下の式で距離Dst1を求めることができるということです。
Dst1=E/58[cm]
原理から考察してみましょう。Echo端子は、超音波を発信するとHIGHとなり、どこかに反射して戻って来た超音波を検出すると、LOWになる信号です。つまり、そのHIGHの幅E[μs]を測定すると、音が対象物で反射し、往復して来た時間がわかる、という訳です。
音速をS=340m/sとした場合、対象物までの距離Dst2は、以下式で表すことができます。
2×Dst2=E×S
これをDst2について解き、次元を合わせると、以下の様になります。
Dst2=E×0.017cm=E×17/1000[cm]≒E/58.8[cm]
まあ、音速S=340m/sとした時点で、だいぶ近似されているので、細かいことにウダウダ言っても始まらないのですが、除数が58.8なら、四捨五入して59じゃないのでしょうか?データシートでは58となってますね……。
乾いた空気中の音速を近似するS=331.5+0.61×Tという式があるそうです。この式から逆算すると、Dst1=E/58の式は、T=21.8℃を使った近似、と捉える事ができます。ちなみに、340m/s(Dst2の式)は、T=13.9℃に相当します(寒っ)。部屋の中での測定なら、Dst1=E/58の方が、近い近似なんですね。
測定方法ですが、以下の様なプログラムになります。
uint ultra_sonic (void)
{
// measure distance by using ultrasonic
// return: distance*10[cm]
ulong usec;
ulong d10cm; // distance*10[cm]
// cycle time
msec=millis ();
if (msec - msec_prev<=msec_usnc_cycl)
delay (msec_usnc_cycl - (msec - msec_prev));
// start measure
digitalWrite (pin_TRIG, HIGH);
delayMicroseconds (usec_usnc_puls);
digitalWrite (pin_TRIG, LOW);
usec=pulseInLong (pin_ECHO,HIGH,600*58UL);
d10cm=usec*10/58;
d10cm=(d10cm>0x00ffff)? 0xffff: d10cm;
msec_prev=millis ();
return (d10cm);
}
まず、10μs以上のHIGHパルス(usnc_puls)をTrig端子に出力します。そうしたら、Echo端子にHIGHパルスが出力されてくるはずですので、その長さをpulseInLong()関数で測定します。測定した時間はμsですので、×170/1000倍して、×10cm単位で求めた距離を返しています(⇒ すみません、回りくどかったですね(1^^;)、つまるところ[mm]単位で返します)。そこまでの精度は持っていないかもしれませんが、四捨五入するなり、切り捨てるなりして使ってください。
データシートにサイクルタイム(MIN 60ms)の規定があったので、その処理も入っています。
では、実験してみましょう。プログラムは、先ほど焦電型赤外線(PIR)センサで示したプログラム(test_pir_usnc.ino)に既に組み込んであります。
超音波センサのTrig端子をArduinoのD4端子に、Echo端子をD5端子にそれぞれ接続してください。
Arduino | 接続 | 超音波センサ |
5V | → | Vcc |
GND | ⇔ | Gnd |
D4 | → | Trig |
D5 | ← | Echo |
プログラム(test_pir_usnc.ino)をArduinoへコンパイル&書き込みしたら、いつものようにシリアルモニタを開いてください。
実験サンプル:【 】内はシリアルモニタからの送信データ
【trg】
OK: dist.=12.4[cm]
超音波センサの向いた方向にある、一番近い障害物までの距離が表示されたらOKです。データシートによると、距離測定可能範囲は2cm〜400cmまでとなっています。
障害物との距離を定規で測っておき、超音波センサで測定してみたところ、〜20cmくらいまでは結構精度よく測定できているようでした(cmオーダ)。それ以上の長さは、正確に測量するのも大変だったので試していません。ただ、シリアルモニタの表示値で400cmを少し超えると、0になったり、明らかに短い距離が出てきたりと、正しくない値が表示されるようになりました。データシートに記載された、スペック通りの挙動だと思われます。
ここから先は「お遊び」です。「#3-6 ステッピングモータ」で回したステッピングモータに、超音波センサを取り付け、距離を測定しながら360°回転させてみます。
ステッピングモータに超音波センサを取り付けるために作成した治工具がこちら(↓)。
出来栄えは何だかアレな感じですが……。こういう工作は苦手なのでご勘弁をm(1__)m。
Arduinoとステッピングモータドライバモジュールとの接続は以下の通りとしてください。
Arduino | 接続 | ドライバモジュール | 接続 | 電源モジュール | 接続 | ステッピングモータ |
D6 | → | IN1 | ||||
D8 | → | IN2 | ||||
D7 | → | IN3 | ||||
D9 | → | IN4 | ||||
コネクタ(メス) | → | コネクタ(オス) | ||||
+電源 | ← | 5V | ||||
GND | ⇔ | −電源 | ⇔ | GND |
ステッピングモータの電源はArduinoではなく、ACアダプタを繋いだ電源モジュールから供給します(下図)。
では実験です。最初はどこかの部屋を丸ごと360°測定してみようかと思ったのですが、さすがに家具や何かで遮られて、期待値をモデリングするのが難しそうだったので、段ボール箱に測定装置(ステッピングモータ+超音波センサ)を入れて、段ボール箱までの距離を360°測定してみることにしました。
完成プログラム:test_pir_usnc.ino
実験サンプル:【 】内はシリアルモニタからの送信データ
【rdr】
RADR
0,158 …… <角度[°]>,<距離[mm]>
1,158
2,157
4,163
5,161
7,162
:
END
超音波センサに接続したケーブルがねじれるので、そのまま何回も回転させることはできません。一つの解決策として、逆回転しながら距離測定するコマンド【rdr-】も実装しておきました。【rdr-】コマンドでは、シリアルモニタに送られてくる角度データ(RADR〜END)がマイナス値になります。
出力データを処理するOlive+のスクリプトをリンクしておきます。
処理スクリプト:test_pir_usnc.olv
測定データサンプル:test_pir_usnc.txt
シリアルモニタのログを"test_pir_usnc.txt"と言う名前のテキストファイルにコピー&ペーストしてください(RADR〜ENDの間)。Olive+のスクリプトファイル(test_pir_usnc.olv)と同じディレクトリに上のテキストファイルを格納して、スクリプトを実行すると、以下のようなグラフが表示されるはずです。
上の図とデータは、約30×33[cm]の段ボール箱の中央付近にステッピングモータ+超音波センサを配置した状態で、360°距離を測定したデータを示しています。内側の緑円がセンサから10cmの距離、外側の円が20cmの距離を表しています。青い矩形は、実験に使った段ボール箱のモデリングです。
段ボールの4つの辺がしっかりと測定できています。ただ、4隅の角の辺りは、うまく測定できていないようです。角の辺りでは、超音波が複雑に反射して、測定を誤っているのではないか?と推測しています。
ステッピングモータだと、回転角度を制御しながら回すことができるので、こういう用途にはもってこいですね。
元々は、筆者がPCの前に座っていることを検知して、近くの扇風機や天井灯などを自動制御したいと思い、その目的のために焦電型赤外線(PIR)センサを使おうと、購入しました。
扇風機も天井灯も赤外線リモコンで操作可能なタイプなので、「#3-9 赤外線リモコンの調査」で評価した赤外線受信モジュールで送信データを解析すれば、制御に必要な信号(赤外線)を送信できるだろうし、DIYにはちょうど良いなと。
しかし、今回評価してみて、焦電型赤外線(PIR)センサだと「熱源の動き」を検知するので、PCの前で何か考え事をしていた場合など、暫く動かなかったときに誤判定されるだろうし、あまり、用途に合っていないことが分かりました。
焦電型赤外線(PIR)センサは、熱源の動きを検知して撮影する、動物カメラとか、防犯カメラとか、良く応用されている、人が通った時に点灯する照明器具とか、そういう用途には向いているようですが……。
当初はノーマークだったのですが、Iduinoスタータキットにたまたま含まれていた超音波センサの方が、筆者の当初目的に近いことが分かりました。
「PCの前に座る」というピンポイントなら、超音波センサで距離を測定して判定した方が良さそうです。人が座っているときは比較的近い距離が測定でき、離席している間は後ろの壁までの距離を測定するので遠くなる、ことで人が居るかどうかを検知できます。
目的達成までには、赤外線リモコンの送信など、まだまだクリアしなければならない課題が山積みです。