micro:bit Makecodeで超音波距離センサ(RCWL-1601)の動作テスト

2021.10.06 Coskx Lab  

1 はじめに

micro:bitに超音波距離センサ(RCWL-1601)を接続して周囲の対象物までの距離を測定します。





超音波距離センサRCWL-1601を使うと,超音波パルスの往復時間を測定し,4m程度までの距離を測定することができます。
超音波距離センサを基板上に取り付けた製品はHC-SR04もありますが,動作電圧5V仕様なので,micro:bit3.3Vとは相性が悪いのでここでは扱いません。


2 使用環境

3 端子の割り付け

RCWL-1601に測距指示を与えたり,超音波反射往復時間のパルスを測定するために,2つの信号線を使います ひとつはmicro:bitからセンサへのトリガ信号,もう一本はセンサからmicro:bitへの信号で,超音波反射波の往復時間を表すエコー信号です。 ここでは,トリガ信号のためにmicro:bit端子P9をエコー信号のためにP15を使うことにします。 この2つ以外でも使用されていない端子であればどこでもよい(例えばP2など)です。この段階でどの端子を使うか決めておき,ケーブル接続においても,プログラムにおいてもP9とP15を使うことで一貫している必要があります。
参考 micro:bit端子一覧

4 接続

センサの4つの脚のところに書いてある,Vcc,Trig,Echo,Gndはそれぞれ3.3V,トリガ信号入り口,エコー信号出口,グランド(0V)です。
この4本でmicro:bitに接続します。
micro:bitの3VのところはUSB電源供給時に3.25V供給されていました。電源3.3Vにはこれを使います。
またTrigはP9に,EchoはP15につなぐことを「3.端子の割り当て」で決めました。 すなわち,次のようにつなげることを目指せばよいことになります。




端子に直接はんだ付けしてしまってもよいのですが,そのようなことはしたくないため,RCWL-1601には,オスーメスタイプのジャンプワイアを取り付けます。




そして,micro:bitの端子を引き出して扱いやすくなっているブレイクアウトボードなどを使います。
Sparkfunのブレイクアウトボードを使った場合には次のような配線になります。




実際の配線は次のようになります。



5 テストプログラム(拡張機能を使って楽する)

拡張機能「Sonar」を使うと,超音波センサの動きを知らなくても,簡単に超音波センサで前方の物体までの測距出来ます。
MakeCode画面で「拡張機能」を開き, 拡張機能の一覧の中に「sonar」が見つかるので,これを取り込ます。



次のようにSnoarからブロックを取り出して,最初に設計した通り「トリガ信号のためにP9をエコー信号のためにP15を使う」ように選択し設定します。



これで,コンソールにcm単位の距離が表示されるようになります。

6 テストプログラム(自分で超音波センサを操る関数を作る)

MakeCodeを使います。
拡張機能を取り入れてもよいのですが,ここでは自分で超音波センサで距離を測定する関数を作ってみます。
端子割り当ての段階でP9,P15を使うことにしたので,関数内でトリガを与える時はP9を,エコーを測定するときはP15を使うように指示します。
micro:bit側ではP9に0(Low,0v)の出力を確保し,20μ秒のパルスを発生します。
20μ秒のパルスの発生は次の3ステップで出来ます。
(1)P9に1(High,3.3v)を出力
(2)20μ秒間何もしない
(3)P9に0(Low,0v)を出力
このトリガパルスによって,超音波距離センサは超音波を短い時間発射します。
超音波距離センサは発射から反射波を受信するまでの間,エコー信号を1(High,3.3v)にします。
そこでmicro:bitではP15を監視し,戻ってきた正のパルスのパルス長を測定します。(これは1命令で行われます。)
なお,正のパルスとは0(Low,0v)→1(High,3.3v)→0(Low,0v)と変化するパルスです。

距離は往復にかかった時間×音速÷2で求められます。
ただし,往復にかかった時間の単位はμ秒になっています。音速は441m/sとしています。
距離をcmで小数第1位までで表すことにします。
またこのソースコードでは1回の測定につき,50msのpauseが組み込まれています。すなわち,最低でも50msの間隔を空けて使うようになっています。




コピペする場合は,次のソースコードをPythonソースとして貼り付ければ完了です。

dst_duration = 0
def DistanceInCentimeters():
    global dst_duration
    pins.digital_write_pin(DigitalPin.P9, 0)
    control.wait_micros(2)
    pins.digital_write_pin(DigitalPin.P9, 1)
    control.wait_micros(20)
    pins.digital_write_pin(DigitalPin.P9, 0)
    dst_duration = pins.pulse_in(DigitalPin.P15, PulseValue.HIGH)
    basic.pause(50)
    return Math.round(dst_duration * 0.172) / 10

def on_forever():
    serial.write_line("" + str(DistanceInCentimeters()))
basic.forever(on_forever)

7 測定限界付近での不自然さ解消

「5 テストプログラム(拡張機能を使って楽する)」「6 テストプログラム(自分で超音波センサを操る関数を作る)」での測定は測定限界内(4m程度)であれば問題なく動作します。
測定限界内に何もないと,超音波パルスが戻ってこないため,使用されている時間測定関数pins.pulse_in()がタイムアウトを起こします。関数タイムアウト設定はデフォルトで500cmを限界と想定されています。タイムアウトが起きると,測定距離が0になってしまいます。(本当の0cmと区別が付かず,ちょっと不便です。)
また超音波距離センサ(RCWL-1601)のハードウエアのタイムアウトは800cmが想定されているようです。

超音波パルスが戻ってこない状態では,時間測定関数pins.pulse_in()が500cmに対応する時間2900μsで戻ってきて0を返してきます。その後,超音波距離センサ(RCWL-1601)はまだ計測をあきらめておらず,800cmに対応する時間46400μsで計測を打ち切るようになっています。
そのため,pins.pulse_in()のタイムアウト動作が不安定になってしまいました。そこで,エコーパルス測定に時間がかかりすぎていたら,測定限界値を距離として返してくるようにしました。
maxCmDistanceの引数に測定最大距離(通常は500)を与えます。
なお,offsetの引数は測定0点の位置を補正するための値です。
以下が作成した関数です。




コピペする場合は,次のソースコードをPythonソースとして貼り付ければ完了です。
ただし,スース末尾の「URS_echolength = 0」と「URS_time0 = 0」は関数の外に置かれる変数定義です。

def DistanceInCentimeters(maxCmDistance: number, offset: number):
    global URS_time0, URS_echolength
    pins.digital_write_pin(DigitalPin.P9, 0)
    control.wait_micros(2)
    pins.digital_write_pin(DigitalPin.P9, 1)
    control.wait_micros(20)
    pins.digital_write_pin(DigitalPin.P9, 0)
    URS_time0 = input.running_time_micros()
    URS_echolength = pins.pulse_in(DigitalPin.P15, PulseValue.HIGH)
    if maxCmDistance * 58 < input.running_time_micros() - URS_time0:
        return maxCmDistance
    return Math.round(URS_echolength * 0.0172) + offset

URS_echolength = 0
URS_time0 = 0

8 超音波距離センサ使用上の注意

  1. 超音波距離センサは超音波パルスが音速で往復移動する時間を計測することで距離を測定しています。ところが音速は気温などにより変化します。
    距離を正しく測定するには気温補正が必要です。
    しかし,温度変化が小さな状況では,気温に起因する誤差は含むものの,複数回の測定において,再現性のある値が得られます。
    超音波距離センサをbicro:bitに接続して,連続距離測定しているときは多少の測定値の変動はありますが,特に問題は感じないと思います。
  2. 超音波距離センサはアナログ回路であるため。電気的なノイズに弱い性質があります。
    そのため,モータなどと同時に使用する際は,まれに異常な測定値を得てしまうことがあります。
    まれにしか起こらない事ですが,それに備えた対策をしておかないと,問題を生ずることがあります。
  3. 超音波距離センサは超音波を使用しています。センサの形状をみると,センサの正面にあるものまでの距離を測定してくれそうに思います。
    ところが超音波は正面にのみに発射されているわけではありません。
    センサ正面約1mの物体までの距離を測定しようとしたら,正面から45度方向距離50cmの物体からの反射波をとらえて,50cmの距離を測定してしまうといった事も起こります。
    (スピーカから発生する音はスピーカ正面でしか聞こえないわけではないのと同じです。超音波は可聴域の音波より指向性は高いのですが,それほど際立っているわけではありません。)
    測定に当たっては,このような事も考慮し,超音波センサの性能を予め測定して置くほうが良いでしょう。

9 まとめ

超音波距離センサ(RCWL-1601)を使用して,4m程度までの距離測定ができました。

注意 超音波距離センサ(RCWL-1601)を連続使用するときは,最低でも20msは間隔をとる必要があります。。超音波距離センサ(RCWL-1601)が正しく動作するためには超音波パルス発生のための充電が必要で,充電にために20msが必要です。特に測定値の平均化のときは,平均化が無意味になってしまうので注意が必要です。(「補足 2 短インターバル繰り返し測距」参照)


発展 サーボモータとの組み合わせ

サーボモータに超音波距離センサを載せて周囲をスキャンして物体認識 



補足 1 トリガ信号線 エコー信号線の観察

オシロスコープでトリガ信号線(micro:bit P9)およびエコー信号線(micro:bit P15)を観察してみました。
トリガ信号線ではmicro:bitからセンサに向けての測定要求パルス(20μ秒のパルスなので線にしか見えません)を送信し,エコー信号線ではセンサからmicro:bitに向けた「超音波一往復の時間を表すパルス信号」が送られてきます。
この例では「超音波一往復の時間を表す信号」の長さは約9m秒なので,音速を344m/sとすると距離は約150cmとなります。



補足 2 短インターバル繰り返し測距

超音波距離センサは,超音波パルスを発射しますが,そのために電気エネルギを蓄えて,一気に超音波パルスを発射します。
電気エネルギを蓄えるには,しばらく時間がかかるため,ある程度のインターバルが必要となります。
超音波距離センサを固定して(取得されるべきセンサ値はほとんど同じ値になるはず),
 (1)インターバル無し
 (2)インターバル10ms
 (3)インターバル20ms
で回連続測距し,約130cmを正しく測距できるか確かめてみました。
拡張機能「sonar」には,測距操作中にpause(一次停止)が含まれていませんので,このテストに適しています。

(1)インターバル無し(測定間隔ほぼ0)
16回の測定するときは,時間のかかる測定値表示をせず,配列に測定値を保存して,最速で測定します。測定終了後に,配列に保存されていた測定値をコンソールに表示する手法をとりました。




その結果は次の通りになりました。
最初の一回は,正しく測距(約130cm)出来ていますが,それ以降は正しく測距できていません。
最初は,電気エネルギの蓄積があるので,正しく測距出来,それ以降は電気エネルギの蓄積が間に合わず,正しく測距出来なくなると考えられます。 これでは使えません。

started
135
120
60
37
58
40
56
41
55
41
55
41
55
43
53
43

(2)インターバル10ms(測定間隔10ms)
16回の測定するときは,時間のかかる測定値表示をせず,配列に測定値を保存して,10msのpause(一時停止)を行っています。測定終了後に,配列に保存されていた測定値をコンソールに表示する手法をとりました。




その結果は次の通りになりました。
最初は正しく測距(約130cm)出来ていますが,それ以降は1つおきにやや短めに測距できています。
最初は,電気エネルギの蓄積があるので,正しく測距(約130cm)出来,それ以降は電気エネルギの蓄積が間に合わず,正しく測距出来なくなると考えられます。 これでは使えません。

started
129
129
56
123
55
122
55
123
55
123
56
123
56
123
55
124

(3)インターバル20ms(測定間隔20ms)
16回の測定するときは,時間のかかる測定値表示をせず,配列に測定値を保存して,20msのpause(一時停止)を行っています。測定終了後に,配列に保存されていた測定値をコンソールに表示する手法をとりました。




その結果は次の通りになりました。
電気エネルギの蓄積が間に合っており,正しく測距できています。

started
130
130
130
130
130
130
130
130
130
130
130
130
130
130
130
130

このテストから,超音波センサRCWL-1601での測距には,20msほどのインターバルが必要なことがわかりました。
「6 テストプログラム」で作成した超音波センサを操る関数「DistanceInCentimeters」では内部に50msecのpauseが入っています。50msは長すぎであることがわかりました。
なお,「ずっと」ブロックでは舞台裏において20msのpause(一時停止)が行われているため,「ずっと」ブロック中に一回だけ測距を実行する場合は,プログラム中にpause(一時停止)を書かなくても正しく測距できます。

参考 「ずっと」ブロックの動作