【ラズパイ電子工作】超音波センサで距離を読み取る方法(HC-SR04)

【ラズパイ電子工作】超音波センサで距離を読み取る方法(HC-SR04)

ラズベリーパイは基板上に「GPIO」と呼ばれる、ラズベリーパイで作成したプログラムから信号の入力信号の出力が行えるピンが存在します。

信号の入力とは「スイッチのON/OFF」「温度計で室温の計測」といった、ラズベリーパイの外の情報を入力(インプット)してプログラム上で使用することです。

対して、信号の出力とは「LEDを点灯させる」「モータを回す」といった、ラズベリーパイで制御した結果を外に出力(アウトプット)することです。

この記事では、ラズベリーパイのプログラム(Python)から超音波センサを用いて距離を測定する方法を解説します。

注意
この記事中に記載されている内容はソースコード含めて電気設計人が自己流で行ったものです。一般的な方法とは相違がある可能性がありますので予めご了承ください。

1. 完成イメージ(超音波センサで距離を読み取る)

この記事で完成するものは以下のようになります。

10_完成イメージ
10_実行結果

超音波センサでマウスまでの距離を測定して、距離(cm)を実行結果に表示します。

2. 使用する部品

今回使用する部品は以下の通りです。

ラズベリーパイ本体(モデル3B+)

20_ラズベリーパイ3B+

『Raspberry Pi 3 Model B+』を使用します。※2020年7月時点で最新はRaspberry Pi 4になります。

ブレッドボード

20_ブレッドボート

LEDや抵抗といった各種部品や(後述する)ジャンパ線などを穴に差し込み、部品間を電気的に接続する板(ボード)です。

電子工作をする上で必須の部品です。

ジャンパ線

20_ジャンパ線

ブレッドボートに差し込み、電子部品の間を電気的に接続します。

↑の写真では両側が「ピン」になっており、ブレッドボートの穴に差し込んで使用します。

超音波センサ(HC-SR04)

20_超音波センサHC-SR04

HC-SR04は、超音波が反射する時間を利用して非接触で距離を測定するモジュールです。

「トリガー」ピンの状態をHighにすると超音波パルスが送信され、物体に反射して戻ってきた信号を受信します。超音波パルスを『送信した時間』『戻ってきた信号を受信した時間』の差分を求めることで距離を測定します。

ラズパイのGPIOにて「トリガー」ピンを出力、「エコー」ピンを入力に割付けて使用します。

HC-SR04の主要なスペックは以下のようになります。

測定範囲:2~400cm
電源電圧:DC5.0V
動作電流:15mA
動作周波数:40kHz
トリガー信号:10μs

抵抗器

抵抗器

LEDなどの電子部品は適した電圧と電流値が決められており、既定の範囲を超えると壊れてしまう恐れがあります。

抵抗器は電流の流れを妨げ、電子部品に流れる電流を抑える役割を担います。

今回は1kΩ(オーム)の抵抗器を使用します。

オススメの電子工作セット

電子工作をするため、これまで解説した部品の他にもスイッチやセンサなど、色々な部品が必要になってきます。個々で購入するには手間がかかるため、最初はセット品を購入することをおススメします。

電気設計人
電気設計人

私は以下のセット品を購入しました!

また、これからラズベリーパイを購入する場合、ラズベリーパイ本体を含めたセット品を購入することをおススメします。

ラズベリーパイ本体を収めるケースや、OSをインストールするためmicroSDカードなど必要なものを個々に購入する手間を省くことができます。

3. 回路図・配線の様子

ラズベリーパイのプログラム(Python)から超音波センサを用いて距離を測定する回路を解説します。

回路図

回路図は以下のようになります。

30_回路図

超音波センサHC-SR04には以下の4ヶのピンがあります。

  • VCC:DC5.0V電源(一番左側)
  • Trig:トリガー信号
  • Echo:エコー信号
  • GND:グランド(一番右側)

ラズベリーパイとHC-SR04を以下のように接続します。

HC-SR04ラズベリーパイジャンパ線
VCC:5V:赤色
Trig:GPIO 27番ポート(13番ピン):青色
Echo:GPIO 18番ポート(12番ピン)※抵抗経由:黄色
GND:GND:黒色

ジャンパ線は上記の色でなければいけない訳ではありません。

Trigに接続するGPIO 27番ポートはラズベリーパイからトリガーを出力するため出力モードとして使用します。

対して、Echoに接続するGPIO 18番ポートはHC-SR04から”受信した”信号をラズベリーパイが入力するため入力モードとして使用します。

HC-SR04の動作電圧は5Vに対して、ラズベリーパイのGPIOは3.3Vになります。Echoをラズベリーパイがそのまま受けると、GPIOが破損する恐れがあります。そこで、抵抗器を用いて分圧します。

配線の様子

配線の様子です。こんな感じになりました。

↑では、フラットケーブルでGPIOの全ピンをブレッドボードに接続しています。回路図と実際の配線は異なる部分がありますがご了承ください。

※電気的には「回路図」と同じ意味です。

4. プログラム(Python)

超音波センサを用いて距離を測定するプログラム(Python)は以下のようになります。

ソースコード

#必要なモジュールをインポート
import RPi.GPIO as GPIO             #GPIO用のモジュールをインポート
import time                         #時間制御用のモジュールをインポート
import sys                          #sysモジュールをインポート

#ポート番号の定義
Trig = 27                           #変数"Trig"に27を代入
Echo = 18                           #変数"Echo"に18を代入

#GPIOの設定
GPIO.setmode(GPIO.BCM)              #GPIOのモードを"GPIO.BCM"に設定
GPIO.setup(Trig, GPIO.OUT)          #GPIO27を出力モードに設定
GPIO.setup(Echo, GPIO.IN)           #GPIO18を入力モードに設定

#HC-SR04で距離を測定する関数
def read_distance():
    GPIO.output(Trig, GPIO.HIGH)            #GPIO27の出力をHigh(3.3V)にする
    time.sleep(0.00001)                     #10μ秒間待つ
    GPIO.output(Trig, GPIO.LOW)             #GPIO27の出力をLow(0V)にする

    while GPIO.input(Echo) == GPIO.LOW:     #GPIO18がLowの時間
        sig_off = time.time()
    while GPIO.input(Echo) == GPIO.HIGH:    #GPIO18がHighの時間
        sig_on = time.time()

    duration = sig_off - sig_on             #GPIO18がHighしている時間を算術
    distance = duration * 34000 / 2         #距離を求める(cm)
    return distance

#連続して値を超音波センサの状態を読み取る
while True:
    try:
        cm = read_distance()                   #HC-SR04で距離を測定する
        if cm > 2 and cm < 400:                #距離が2~400cmの場合
            print("distance=", int(cm), "cm")  #距離をint型で表示
        time.sleep(1)                          #1秒間待つ

    except KeyboardInterrupt:       #Ctrl+Cキーが押された
        GPIO.cleanup()              #GPIOをクリーンアップ
        sys.exit()                  #プログラム終了

超音波センサHC-SR04の「トリガー」ピンにGPIOからHighに出力して、「エコー」ピンに信号が戻ってきた時間を計測して距離を求めます。

プログラムの解説

#必要なモジュールをインポート
import RPi.GPIO as GPIO             #GPIO用のモジュールをインポート
import time                         #時間制御用のモジュールをインポート
import sys                          #sysモジュールをインポート

2~4行目で今回必要な「モジュール」を宣言します。


#ポート番号の定義
Trig = 27                           #変数"Trig"に27を代入
Echo = 18                           #変数"Echo"に18を代入

変数”Trig”と”Echo”に数値を代入します。この数値はGPIOのポート番号として扱い、後からポート番号を変更する場合はこの数値を変更します。

今回は『GPIO 27番ポートをトリガー』『GPIO 18番ポートをエコー』として使用します。


#GPIOの設定
GPIO.setmode(GPIO.BCM)              #GPIOのモードを"GPIO.BCM"に設定
GPIO.setup(Trig, GPIO.OUT)          #GPIO27を出力モードに設定
GPIO.setup(Echo, GPIO.IN)           #GPIO18を入力モードに設定

GPIOの設定を行います。

GPIO.setmode(GPIO.BCM)は、GPIOをポート番号で扱う方法に設定します。

GPIO.setup(Trig, GPIO.OUT)は、GPIO 27番ポートを出力モードに設定します。

GPIO.setup(Echo, GPIO.IN)は、GPIO 18番ポートを入力モードに設定します。


#HC-SR04で距離を測定する関数
def read_distance():
    GPIO.output(Trig, GPIO.HIGH)            #GPIO27の出力をHigh(3.3V)にする
    time.sleep(0.00001)                     #10μ秒間待つ
    GPIO.output(Trig, GPIO.LOW)             #GPIO27の出力をLow(0V)にする

    while GPIO.input(Echo) == GPIO.LOW:     #GPIO18がLowの時間
        sig_off = time.time()
    while GPIO.input(Echo) == GPIO.HIGH:    #GPIO18がHighの時間
        sig_on = time.time()

    duration = sig_off - sig_on             #GPIO18がLowからHighの時間を算術
    distance = duration * 34000 / 2         #距離を求める(cm)
    return distance

HC-SR04にて距離を測定する関数を作成します。

まずHC-SR04の「トリガー」ピンに対して、10μ秒間信号をHighにします。(17~19行目)

次に「エコー」ピンの状態を監視して、LowからHighになるまでの時間を測定します。(21~24行目)

最後に距離を求める演算を行い、変数”distance”を戻り値に返します。

distance = duration * 34000 / 2は、測定した時間を音速で掛けて半分にしています。音速とは約340m/秒であるため、cmに直すと34000となります。


#連続して値を超音波センサの状態を読み取る
while True:
    try:
        cm = read_distance()                   #HC-SR04で距離を測定する
        if cm > 2 and cm < 400:                #距離が2~400cmの場合
            print("distance=", int(cm), "cm")  #距離をint型で表示
        time.sleep(1)                          #1秒間待つ

    except KeyboardInterrupt:       #Ctrl+Cキーが押された
        GPIO.cleanup()              #GPIOをクリーンアップ
        sys.exit()                  #プログラム終了

「while文」は条件を指定して、その条件が真の時に繰り返し処理を行うものです。

Python
while 条件式:
    繰り返し処理を行うコード

↑の「条件式」にTrueを指定することにより、条件式は常に真となりwhile文は無限に繰り返します。

メモ
無限に繰り返すことを、無限ループと表現したりします。

ただし、このままではプログラム実行中に無限ループから抜け出す方法がありません。そこでwhile文の中にある「try文」と「except文」を使用します。

このtry-except文を用いることにより、while文の処理は以下のようになります。

Python
while True:
    try:
        繰り返し処理を行うコード
    except KeyboardInterrupt:               #Ctrl+Cキーが押された
        GPIO.cleanup()                      #GPIOをクリーンアップ
        sys.exit()                          #プログラム終了

except KeyboardInterrupt:は、キーボードのCtrl + cキーが押された時にwhile文の繰り返し処理から抜けて上記のプログラム終了の処理を行います。

Ctrl + cキーが押されていないとき、try:文の中の処理を繰り返し行います。(以下で解説)


while文の中の「繰り返し処理を行うコード」は以下の通りです。つまり、Ctrl + cキーが押されるまで以下の処理を繰り返します。

    cm = read_distance()                   #HC-SR04で距離を測定する
    if cm > 2 and cm < 400:                #距離が2~400cmの場合
        print("distance=", int(cm), "cm")  #距離をint型で表示
    time.sleep(1)                          #1秒間待つ

cm = read_distance()は、HC-SR04で測定した距離を変数”cm”に代入します。

HC-SR04の測定範囲は2~400cmのため、if cm > 2 and cm < 400:にて範囲外の値が入力された場合は除外する処理を行います。

測定した距離が2~400cmの範囲内の場合、print("distance=", int(cm), "cm")にて実行結果に表示させます。

time.sleep(1)は何もせずに1秒間待ちます。

5. おわりに

ラズベリーパイのプログラム(Python)から超音波センサを用いて距離を測定する方法を解説しました。

「超音波センサ」と聞くと、最初は身構えてしまいましたが意外と簡単に作成することができました。これから超音波センサを活用して色々と試してみたいと思います。

まだまだラズベリーパイ初心者の私ですが、以下の参考書が大変参考にさせて頂いております。

2冊とも初学者にも易しい内容になっており、ゼロからラズベリーパイを始める方にもオススメできる参考書です。

3 COMMENTS

ty

本ページ参考に実機で試験した所、ソースは以下の正誤があるようです。

誤 duration = sig_off – sig_on 距離が表示されない。値が負となる。
正 duration = sig_on – sig_off 距離が表示、値も正確

以下のサイトを見るとEchoがhighとなる時間が距離を示すため、
式を変更した所、距離が表示されました。
表示と実測を比べると同じ値となりました。
https://101010.fun/iot/arduino-hc-sr04.html

回路は抵抗の大きさ含め同じで作成
ただしRaspberry Pi 3 Model Bを使用したため、環境は異なります。

返信する
とりてん

このコードを実行すると必ず距離がマイナスになるようですが。
duration = sig_off – sig_on で時間の算出結果がマイナスになるためであり、
duration = sig_on – sig_off とすべきではありませんか?

返信する
シモ

except KeyboardInterrupt: #Ctrl+Cキーが押された
GPIO.cleanup() #GPIOをクリーンアップ
sys.exit() #プログラム終了
プログラムを実行するとエラーがでる。
Traceback (most recent call last):
File “/usr/lib/python3.7/ast.py”, line 35, in parse
return compile(source, filename, mode, PyCF_ONLY_AST)
File “/home/pi/hcsr04.py”, line 34
except KeyboardInterrupt:
^
IndentationError: unexpected unindent
このようにでる。
原因がわからない

返信する

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です