2017年10月15日日曜日

測距センサー(HC-SR04)とRaspberry Piをつないでみる

最近週末は安物センサーと Raspberry pi で遊んでいます。

今回は距離を測る HC-SR04 というセンサーを Raspberry pi から使ってみます。このセンサーは一時期センサー自体が固まる不良ロッドが広く流通した影響かとても安く出回っています。きちんと動作するものが欲しければ、当時リコール対応をした秋月電子やスイッチサイエンスなど信頼のおけるところから入手してください。

ちなみに私はAmazonの怪しい販売店から150円以下で買いました。

センサーの仕様と外観

見た目は下記のような感じです。

表面


裏面



仕様は下記のような感じで、分解能が0.3cmと割とおおざっぱです・・・。
動作電圧 DC5V
待機電流 2mA以下
有効角度 15°以下
測距距離 2cm~450cm
分解能 0.3cm


配線

ピンは下記のような感じで配線します。GPIOは余っている場所ならどこでもいいです。今回はGPIO26とGPIO19を使いました。
HC-SR04 Raspberry Pi 3
Trig(Output) 37 pin (GPIO26)
Echo(Input) 35 pin (GPIO19)
Ground 06 pin (GND)


ブレッドボード

下図のように配線しました。センサーのEchoからは5Vで入力がありますが、Raspberry pi側の入力は最大3.3Vですので、抵抗は入れなくても動くと記載されているブログもありましたが念のため抵抗を入れてあります。


測距方法の概要

概要としては下記のような手順です。
  1. Trigに10u秒以上をHIGHにすると、測距が開始されます。
  2. EchoがLOWであることを確認します。
  3. 音波が対象物に当たって跳ね返ってくるまでの時間、EchoがHIGHになり、その後EchoがLOWになります。
  4. HIGHになっていた時間と音速を掛けて、往復の距離が出ますので2で割ることで片道の距離を求めます。
下記のような図をイメージしていただければよろしいかと思います。

実装

1. はTrigをHIGHにして10usecの間SleepしたあとLOWにすれば計測が始まります。

2. と 3. の部分については、ArduinoにpulseInというC言語の関数がありますので、このソースを参考にPythonに移植してみます。

pulseIn(pin, value, timeout)

となっていますので、引数もこれを模倣することにします。

4. の音速についてはWikipediaに近似式が「1気圧の乾燥空気では 331.5 + 0.61t」と書いてありましたので、これを使うこととします。ちなみにtは温度です。

温度は固定で入れるか、温度センサーがついていれば、その値を入れてみてもいいかもしれません。
# そもそも分解能が0.3cmと結構おおざっぱなので固定15℃で十分かも

コード

pulse_inの泥臭さ・・・。まぁ、サイクル単位でコード書けるArduinoと比較してしまうとこんなもんかなという感じで、PythonだとArduinoのようなリアルタイムな感じのコードは難しいですね。
hc_sr04_test.py
import RPi.GPIO as GPIO
import time


def pulse_in(pin, value=GPIO.HIGH, timeout=1.0):
    """
    ピンに入力されるパルスを検出します。
    valueをHIGHに指定した場合、pulse_in関数は入力がHIGHに変わると同時に時間の計測を始め、
    またLOWに戻るまでの時間(つまりパルスの長さ)をマイクロ秒単位(*1)で返します。
    タイムアウトを指定した場合は、その時間を超えた時点で0を返します。
    *1 pythonの場合はtimeパッケージの仕様により実装依存ですが、概ねnanosecで返ると思います。
    :param pin: ピン番号、またはGPIO 番号(GPIO.setmodeに依存。)
    :param value: パルスの種類(GPIO.HIGH か GPIO.LOW。default:GPIO.HIGH)
    :param timeout: タイムアウト(default:1sec)
    :return: パルスの長さ(秒)タイムアウト時は0
    """
    start_time = time.time()
    not_value = (not value)

    # 前のパルスが終了するのを待つ
    while GPIO.input(pin) == value:
        if time.time() - start_time > timeout:
            return 0

    # パルスが始まるのを待つ
    while GPIO.input(pin) == not_value:
        if time.time() - start_time > timeout:
            return 0

    # パルス開始時刻を記録
    start = time.time()

    # パルスが終了するのを待つ
    while GPIO.input(pin) == value:
        if time.time() - start_time > timeout:
            return 0

    # パルス終了時刻を記録
    end = time.time()

    return end - start


def init_sensors(trig, echo, mode=GPIO.BCM):
    """
    初期化します
    :param trig: Trigger用ピン番号、またはGPIO 番号
    :param echo: Echo用ピン番号、またはGPIO 番号
    :param mode: GPIO.BCM、または GPIO.BOARD (default:GPIO.BCM)
    :return: なし
    """
    GPIO.cleanup()
    GPIO.setmode(mode)
    GPIO.setup(trig, GPIO.OUT)
    GPIO.setup(echo, GPIO.IN)


def get_distance(trig, echo, temp=15):
    """
    距離を取得します。取得に失敗した場合は0を返します。
    :param trig: Trigger用ピン番号、またはGPIO 番号(GPIO.setmodeに依存。)(GPIO.OUT)
    :param echo: Echo用ピン番号、またはGPIO 番号(GPIO.setmodeに依存。)(GPIO.IN)
    :param temp: 取得可能であれば温度(default:15℃)
    :return: 距離(cm)タイムアウト時は 0
    """

    # 出力を初期化
    GPIO.output(trig, GPIO.LOW)
    time.sleep(0.3)
    # 出力(10us以上待つ)
    GPIO.output(trig, GPIO.HIGH)
    time.sleep(0.000011)
    # 出力停止
    GPIO.output(trig, GPIO.LOW)

    # echo からパルスを取得
    dur = pulse_in(echo, GPIO.HIGH, 1.0)

    # ( パルス時間 x 331.50 + 0.61 * 温度 ) x (単位をcmに変換) x 往復
    # return dur * (331.50 + 0.61 * temp) * 100 / 2
    return dur * (331.50 + 0.61 * temp) * 50


if __name__ == "__main__":

    GPIO_TRIG = 26
    GPIO_ECHO = 19

    init_sensors(GPIO_TRIG, GPIO_ECHO)
    while True:
        print("距離:{0} cm".format(get_distance(GPIO_TRIG, GPIO_ECHO)))
        time.sleep(2)

実測!

最後に実測してみます。
実行は、sudoした上で実行してください。
sudo python3 hc_sr04_test.py

終了はCtrl + Cで。
15cmくらいのところに障害物を置いて距離を測ってみたところ下記のような感じでした。値をそのまま使ってしまうと結構ばらつきますね。もともと分解能が0.3cmと記載されていましたが、それ以上にひどい結果です。もう少し正確な値が必要であれば、前回との差分や平均をとるようなことも考えないといけないかもしれません。
距離:13.887083530426025 cm
距離:15.341055393218994 cm
距離:15.258443355560303 cm
距離:15.250182151794434 cm
距離:15.179961919784546 cm
距離:15.22952914237976 cm
距離:15.390622615814209 cm
距離:15.324532985687256 cm
距離:15.353447198867798 cm
距離:15.266704559326172 cm
距離:15.279096364974976 cm
距離:14.936256408691406 cm


1mの場所に物を置いた場合。
距離:101.06756687164307 cm
距離:99.9605655670166 cm
距離:100.59667825698853 cm
距離:101.3938844203949 cm
距離:90.7245397567749 cm
距離:100.15883445739746 cm
距離:100.46449899673462 cm
距離:100.47689080238342 cm
距離:100.50580501556396 cm
距離:100.8569061756134 cm
距離:100.62146186828613 cm

0 件のコメント:

コメントを投稿