前回はセンサーとしてタクトスイッチを使いました。今回はもう少しセンサーらしい?超音波距離センサーHC-SR04を使って距離の測定をしてみます。
HC-SR04のハードウェア仕様
超音波距離センサーHC-SR04のハードウェアの仕様を確認しようと秋月電子の商品ページからデータシートのリンクをクリックすると中国語の2020年版のマニュアルが開きました。これによると距離を測定するデバイスが新しくなり、インターフェースがGPIO、UARTそしてI2Cをサポートするようになっていて、電源電圧も3V〜5.5Vとなっています。
ところが手元にあるHC-SR04は2020年より前からあったもので、Arduinoで使っていたもので動作電圧は5Vの筈です。そこで探してみると英語版のUser Guideがありました。こちらを参考にします。
HC-SR04の使い方
HC-SR04は10μs以上のパルスがTrigger端子に入力されると8つの連続したパルス(40kHzの超音波信号)を送出し最後のパルスの立ち下がり後エコー信号をHIGHにし(①)、戻ってきた反射波の立ち上がりを検知するとエコー信号をLOWにします(②)。この①から②までの時間がわかれば音速(34000cm/S)から対象物までの距離が計算できることになります。
プログラムではトリガー信号を生成して出力して、入力されたエコー信号の②の時刻から①の時刻を引いた時間(対象物との往復にかかる時間)に17000を掛けて距離を算出します。
電圧の異なるデバイス間のインターフェース
Raspberry Pi Zero W(以下Pi Zero)とHC-SR04間はPi ZeroのGPIOとHC-SR04のEcho信号、Trigger信号でインンターフェースします。ところが、Pi ZeroのGPIOの電源電圧(BCM2835のVDDIO2)は3.3VでHC-SR04の電源電圧は5VなのでHC-SR04の出力をPi ZeroのGPIOに接続するには考慮しなければならない事があります。
Raspberry Pi Hardwareによると、Pi ZeroのHighレベルの出力電圧Vohの最小値は3Vなので、HC-SR04のHIGHレベルの入力電圧Vihの最小値が3V以下である必要があります。HC-SR04の英語版のUser Guideによると詳細な仕様は書かれていませんがTrigger信号はTTL Pulseとなっています。LS74シリーズのような一般的なTTLデバイスではVihは2Vなのでこれは問題ありません。
前述したようにPi ZeroのGPIOの電源電圧は3.3VでHC-SR04の電源電圧は5Vです。Echo信号をPi Zeroに入力した場合、最大5Vの電圧がかかることになり、BCM2835を壊す可能性があります。そのため5Vから3.3V以下になるようにレベル変換をしなければなりません。そこで最も単純な方法として抵抗分割で行うことにします。
以下の回路図で示すような1kΩと1.5kΩの抵抗で分割します。
この場合Pi Zeroに入力される電圧は5 x 1.5/2.5=3Vとなります。これであればPi ZeroのGPIOの電源電圧3.3V以下なのでOKです。もう一つ確認しておきます。TTLのVohの最小値は2.5Vです。つまり最悪HIGHレベルの出力電圧は2.5Vなのでこの場合のPi Zeroに入力される電圧は2.5 x 1.5/2.5=1.5Vになります。Pi ZeroのGPIOのVihの最小値は1.6Vなので、HC-SR04のEcho信号出力が2.5Vの場合、Pi Zeroは認識できません。
このように最悪の場合を考えるとこの定数では問題がありますが、そこは自作で一品限りということで現物を確認してして良しとします。下のオシロスコープの画像は実際に動作させた時のエコー信号の波形です。CH1(黄)はCH-SR04の出力でCH2(青)はPi ZeroのGPIO23の波形です。CH1は5.36VppでCH2は3.26Vppになっているのが確認できます。
回路図
Trigger信号はGPIO22ピン(物理ピンの15番ピン)に、抵抗分割後のEcho信号はGPIO23ピン(物理ピンの16番ピン)に、GNDは物理ピンの9番ピンにそして5Vは物理ピンの2番に接続します。
HC-SR04の接続
Pi ZeroとブレッドボードのHC-SR04を接続した写真です。測定する対象物となる木片とブレッドボード上のHC-SR04との距離がわかるように金尺を置いています。
ソースコード
ファイル名はtest5.pyです。
#!/usr/bin/python import RPi.GPIO as GPIO # RPi.GPIOモジュールをGPIOとして使用 import time # timeモジュールの読み込み gpio_trig = 22 # トリガーピンのGPIO番号を定義 gpio_echo = 23 # エコーピンのGPIO番号を定義 GPIO.setmode(GPIO.BCM) # GPIO番号で指定する設定 GPIO.setup(gpio_trig, GPIO.OUT) # トリガーピンを出力に設定 GPIO.setup(gpio_echo, GPIO.IN) # エコーピンを入力に設定 try: while True: # トリガー信号の生成 GPIO.output(gpio_trig, GPIO.HIGH) # トリガーピン出力をHIGHにして time.sleep(0.00001) # 10マイクロ秒保持し、 GPIO.output(gpio_trig, GPIO.LOW) # その後LOWにする # エコー信号が0から1になる時刻を記録 while GPIO.input(gpio_echo) == 0: # エコー信号がLOWの間 EndTimeLow = time.time() # 現在時刻をEndTimeLowに代入 # エコー信号が1から0になる時刻を記録 while GPIO.input(gpio_echo) == 1: # エコー信号がHIgHの間 EndTimeHigh = time.time() # 現在時刻をEndTimeHighに代入 duration = EndTimeHigh - EndTimeLow # エコーがHIGHの期間を算出 distance = duration * 17000 # 距離の計算 print("Distance = ", format(distance, '.2f'), "cm") # 計算結果をコンソールに出力 time.sleep(1) except KeyboardInterrupt: GPIO.cleanup() # GPIOを解放
測定した結果はprint分でコンソールに出力します。その際、format関数で小数点以下2桁までを表示するようにしています。format関数の第1引数で出力したい数値を、第2引数で.「出力したい小数点の桁数」fのように指定します。
実行結果
以下、実行結果になります。
金尺の5cmと15cmに木片を置いての測定ですが平均するとそれぞれ5.10cmと15.64cmになりました。対象物が木片で表面が多少ざらついているののと、エコー信号を検知するのをポーリングでしているのででこの程度のばらつきは妥当かなと。
いずれにしても、測定することはできました。