超音波距離センサーHC-SR04をIntel Edison/Galileoで使ってみる

はじめに

マイコンなどの制御で何らかの物体を動かす際には周囲の状況をセンサーで把握する必要が出てくることがよくあります。この際によく利用されるのが距離センサーです。いくつか方式があり、レーザー、可視光、超音波などを使用するものがありますが、今回は超音波を使用する距離センサー「HC-SR04」を使用してみたいと思います。

使用している機材について

  • 本記事で使用している「Intel Edison Kit for Arduino」はインテル株式会社のご厚意により“さかきけい”個人に貸し出されたものです。このため私の勤務先等とは一切関係がありません。
  • 本記事は“さかきけい”が完全に自由意思で書いているもので、インテル株式会社からは何らの要請、制限等は受けていません。
  • 使用している以下のものは市販品を“さかきけい”が購入したものです。
    • Intel Galileo Development Board(Intel Galileo Gen 1ボード)
    • Intel Galileo Gen 2 Board
    • その他、明示していないすべてのパーツ・小物類

前提条件

今回作成する「ライブラリ」は以下の環境で動作確認を行いました:

  • Arduinoソフトウェア開発環境(Arduino IDE)の1.5以降用の「ライブラリ」を作成することを前提としています。それ以前では作成方法が異なります。
  • Intel EdisonおよびIntel Galileo Gen 1 / Gen 2向けに配布されているArduino IDE 1.5.3ベースの「Arduino Software Package Version 1.5.3 – Intel 1.0.4」を使用しました。
  • 「ライブラリ」の動作確認はIntel Galileo Gen 1 / Gen 2およびIntel Edison Moduleを搭載したIntel Edison Board for Arduinoにおいて5Vに設定した状態で確認を行いました。この結果、Intel Galileo Gen 1ボードでは正常に動作しないことを確認しています。
  • 今回の動作確認は主にIntel Galileo Gen 2で実施しました。

超音波距離センサーHC-SR04について

非常によく使われているのを見かける超音波距離センサーである「HC-SR04」ですが、調べてみるとその素性はよくわかりませんでした。入手性はいいですし、あちこちの通販、店舗で比較的安価で手に入ります。しかし、その製造元を調べてみたのですが、いまいちはっきりしません。中国製であることは確かなようですが…。

それはともかくとして、超音波距離センサーHC-SR04の仕様は以下のようになっている模様です:

項目 内容
動作電圧 DC 5V(最低4.5V、典型5.0V、最大5.5V)
非動作時消費電力 2mA未満(最低1.5mA、典型2mA、最大2.5mA)
動作時消費電力 15mA(最低10mA、典型15mA、最大20mA)
測定角度 30°
有効角度 15°未満
有効距離 2cm~400cm
分解能 0.3cm
トリガー入力パルス幅 10μs
超音波周波数 40kHz
サイズ 45mm×20mm×15mm

検出対象(障害物)についてですが、少なくとも0.5平方メートルの表面積があるものが望ましいそうです。距離と角度、素材、そして形状によってやや異なりますが、これよりも小さなものでも必ずしも検出しないわけではありません。

距離検出の仕組み

Trig信号を10μs※1以上Hレベルを保った上でLレベルに下げると8個の40kHzの超音波パルスを送信します。この後Echo信号をHレベルにし、超音波パルスが戻ってくるまでそれを保ちます。そして超音波パルスが戻ってくるとLレベルに下げます。

HC-SR04 タイミング・チャート

つまり、この超音波パルスが戻ってくるまでどのくらいの時間、Echo信号がHレベルになっていたかを計測することで距離を計算することができます。

音速は一般に340メートル/秒として計算することが多いので、これを当てはめると、

Echo出力時間[μs]÷2×340[m/s]÷10000

と計算することでcm(センチ・メートル)単位の距離が計算できます。

最初に2で割っているのはEcho出力時間が超音波の往復時間になっているのを片道にするためです。次の340は音速(メートル/秒)です。そしてセンチ・メートルに合わせるために10000で割って桁を合わせます。

超音波距離センサーHC-SR04とIntel Edison for Arduinoの配線

このHC-SR04は動作電圧が5Vなので、電圧を5Vに設定したIntel Edison Board for Arduinoを使用します。Intel Galileo Gen 2ボードでもコネクターが180度回転している点を気を付ければそのまま使用できます。ただし、Intel Galileo Gen 1ボードではGPIOのアクセス速度がネックとなってしまい、今回のこのメモで紹介している方法では動作しません。

配線は以下のように行います:

Intel Edison Kit for ArduinoとHC-SR04の接続図

Intel Edison for Arudino HC-SR04
5V Vcc
8 Trig
9 Echo
GND Gnd

余談ですが、上記の色については私が今回使用したジャンパー・ワイヤーの色と一致しています。※2

動作確認用のスケッチ

接続が終わったらHC-SR04および接続の動作確認をしてみたいと思います。実行するスケッチは以下のとおりです:

#define TRIGGER_PIN  8
#define ECHO_PIN     9

#define SOUND_SPEED  340.0f

void setup() {
  Serial.begin(115200);
  
  pinMode(TRIGGER_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);

  digitalWrite(TRIGGER_PIN, LOW);
  delayMicroseconds(10);
}

void loop() {
  int duration;
  float distance;
  
  digitalWrite(TRIGGER_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIGGER_PIN, LOW);
  
  duration = pulseIn(ECHO_PIN, HIGH);
  
  if(duration > 0) {
    distance = duration / 2.0f * SOUND_SPEED / 10000.0f;
    Serial.print(distance, 4);
    Serial.println(" cm");
  } else {
    Serial.println("Out of range.");
  }
  
  delay(1000);
}

このスケッチを実行して「シリアルモニタ」を起動すると1秒ごとにHC-SR04から得た距離が以下のような感じで表示されます:

31.5180 cm

HC-SR04の方向をいろいろと変えたり、障害物を置いてみたりするとそのたびに検出する距離が変化するのが確認できるはずです。もしもうまく動かない場合には接続を確認してみてください。

温度と音速の関係

いろいろと試してみると、それなりに誤差が出ているのが確認できるのではないかと思います。先ほどのスケッチでは音速を340メートル/秒と仮定していますが、実際には温度の変化でこの速度は結構変化します。ウィキペディアの「音速」項目にある式「20.055 (x + 273.15 )1/2」を使用して計算してみると、温度によって以下のような差が出ます:※3※4

温度 音速 差分 30cm 50cm 100cm 150cm
10℃ 337.4667 100.000% 30.0000cm 50.0000cm 100.0000cm 150.0000cm
15℃ 340.4333 100.879% 30.2637cm 50.4395cm 100.8791cm 151.3186cm
20℃ 343.3742 101.751% 30.5252cm 50.8753cm 101.7505cm 152.6258cm
25℃ 346.2901 102.615% 30.7844cm 51.3073cm 102.6146cm 153.9219cm
30℃ 349.1817 103.471% 31.0414cm 51.7357cm 103.4715cm 155.2072cm
35℃ 352.0495 104.321% 31.2964cm 52.1606cm 104.3213cm 156.4819cm

10℃と35℃では4.321%の差があります。30cm程度なら誤差は1.2964cmと、用途によっては許容できそうです。150cmでは6.4819cmの誤差で、できれば訂正したいところかと思います。いずれにしても、HC-SR04の分解能が0.3cmであることを考えれば、補正できるならば補正したいというのが正直なところでしょう。

そこで、デフォルトでは15℃を想定し、その他の温度については指定できるようにすることにしました。その温度に応じて音速を計算し、距離の計算はその音速を元に行います。音速の計算式は「20.055 (x + 273.15 )1/2」を採用します。また、これとは別に音速を直接指定できるようにもします。

超音波距離センサーHC-SR04の仕様に合わせた制限の追加

HC-SR04の計測可能距離は2~400cmとされています。この範囲外は計測しても不正確な可能性があります。そこで手持ちのHC-SR04で実際に計測してみたところ、最短距離の2cmは何とも言えない感じです。2cmより若干近いものも計測でき、それよりもさらに距離を縮めると計測した距離が伸びるという結果が得られました。一方で距離を増加させると一定以上先では不正確になります。400cmもの距離は正しく計測できていないように思える結果が得られますが、これが私の環境に固有なことかどうかはいまいち自信を持って言えません。

文末に記載している「参考資料①」のPage 7には「about 150uS-25ms, 38ms if no obstacle(概ね150μs~25ms、38msでは障害物なし)」と記載があり、150μsから25,000μs(=25ms)の間が設計上のEcho出力時間のようです。しかし、150μsで計算すると距離は2cmに届きません。少なくとも100μsから、ということになりそうです。また、38,000μs(=38ms)では障害物の検出なしとしていますが、今回のテストではぴったりとこの値が出ることはありませんでしたし、計測の関係で増減する可能性は高いと言えるでしょう。実際にはこの値を超えると障害物を検出していない状況であるとみなすことができる程度に考えた方がよいようです。このことから、少なくともこれが判定するうえでの上限となるようです。

これらを考慮して、デフォルトでは最短1μs、最長で38,000μsを制限値として採用することにしました。また、これらの範囲は必要に応じて変更できるようにし、計測が不要な範囲外の除外をシンプルにできるようにすることにしました。

ここまでの知見をもとに「ライブラリ」を作成する

仕様が決まったので、それに合わせて「ライブラリ」のプログラムを作成します。

関数(メソッド)ですが、一般にセンサー系は「read」の接頭辞で始めるのがArduino流ですが、今回は読むというよりは処理をして計算して「get(取得)」する感じなので、あえて「get」を接頭辞に使用することにしました。また「基本的に設定値は読めた方がいいよね」という気がしたので「設定した温度と音速は読める」ようにしました。ただし「音速を設定した場合に温度を逆算して設定する」ことはしていません。なぜなら、今回は「あくまでも距離を超音波で計算する際の補正情報として温度と音速を扱うため、それを超えるサポートは行わない」ためです。※5

「ライブラリ」名は「HC_SR04」とすることにしました。というのも、クラス名は「HC-SR04」とするわけにはいかないからです。そこで「-」を「_」に置き換えたわけです。この「フォルダ」の中に各種ファイルを置いていきます。

src/hc_sr04.h

//////////////////////////////////////////////////////////
// Sketch library for HC-SR04 (manufacturer unknown)
// Header file.
// Programmed by KEI SAKAKI. https://kei-sakaki.jp/

#ifndef INCLUDED_KEI_SAKAKI_HC_SR04
#define INCLUDED_KEI_SAKAKI_HC_SR04

#if (ARDUINO >= 100)
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

class HC_SR04 {
  protected:
    int TirggerPin;
    int EchoPin;
    int MinDuration;
    int MaxDuration;
    float Temperature;
    float SoundSpeed;
    float LastDistance;
  public:
    enum {
      MIN_ECHO_DURATION = 1,
      MAX_ECHO_DURATION = 38000
    };
  
    HC_SR04(int TirggerPin, int EchoPin);
    virtual ~HC_SR04();
    virtual void setDurationRange(int max = MAX_ECHO_DURATION, int min = MIN_ECHO_DURATION);
    virtual float getDistance(bool inch = false);
    virtual float getLastDistance(bool inch = false);
    virtual void setTemperature(float Temperature);
    virtual float getTemperature();
    virtual void setSoundSpeed(float SoundSpeed);
    virtual float getSoundSpeed();
};

#endif

src/hc_sr04.cpp

//////////////////////////////////////////////////////////
// Sketch library for HC-SR04 (manufacturer unknown)
// Programmed by KEI SAKAKI. https://kei-sakaki.jp/

#include <hc_sr04.h>

HC_SR04::HC_SR04(int TirggerPin, int EchoPin)
{
  this->TirggerPin = TirggerPin;
  this->EchoPin = EchoPin;
  LastDistance = NAN;

  setTemperature(15.0f);  // Typical temperature of 340 meters per second.
  setDurationRange();

  pinMode(TirggerPin, OUTPUT);
  pinMode(EchoPin, INPUT);

  digitalWrite(TirggerPin, LOW);
  delayMicroseconds(10);
}

HC_SR04::~HC_SR04()
{
}

void HC_SR04::setDurationRange(int max, int min)
{
  if(min < MIN_ECHO_DURATION) {
    min = MIN_ECHO_DURATION;
  }
  if(max > MAX_ECHO_DURATION) {
    max = MAX_ECHO_DURATION;
  }
  
  MinDuration = min;
  MaxDuration = max;
}

float HC_SR04::getDistance(bool inch)
{
  int duration;
  float distance;
  
  digitalWrite(TirggerPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(TirggerPin, LOW);

  duration = pulseIn(EchoPin, HIGH, MaxDuration);
  if(duration >= MinDuration && duration <= MaxDuration) {
    distance = duration / 2.0f * SoundSpeed / 10000.0f;
  } else {
    distance = NAN;
  }
  
  LastDistance = distance;
  
  if(distance != NAN && inch) {
    distance /= 2.54f;
  }
  
  return distance;
}

float HC_SR04::getLastDistance(bool inch)
{
  float distance = LastDistance;
  
  if(distance != NAN && inch) {
    distance /= 2.54f;
  }
  
  return distance;
}

void HC_SR04::setTemperature(float Temperature)
{
  this->Temperature = Temperature;
  setSoundSpeed((float) (20.055f * pow(Temperature + 273.15f, 0.5f)));
}

float HC_SR04::getTemperature()
{
  return Temperature;
}

void HC_SR04::setSoundSpeed(float SoundSpeed)
{
  this->SoundSpeed = SoundSpeed;
}

float HC_SR04::getSoundSpeed()
{
  return SoundSpeed;
}

examples/Simple/Simple.ino

//////////////////////////////////////////////////////////
// Sketch library for HC-SR04 (manufacturer unknown)
// Example Sketch.
// Programmed by KEI SAKAKI. https://kei-sakaki.jp/

#include <hc_sr04.h>

HC_SR04* hc_src04;

void setup() {
  Serial.begin(115200);

  hc_src04 = new HC_SR04(8, 9);
  hc_src04->setTemperature(25.0f);
  Serial.print("Speed of sound : ");
  Serial.print(hc_src04->getSoundSpeed(), 4);
  Serial.println(" m/s");
}

void loop() {
  Serial.print("Distance : ");
  Serial.print(hc_src04->getDistance(), 4);
  Serial.println(" cm");
  delay(1000);
}

library.properties

name=HC_SR04
version=1.0
author=KEI SAKAKI
email=CXE01567@nifty.ne.jp
sentence=HC-SR04 Supersonic Distance Sensor Library
paragraph=Library for distance sensor, corresponding to adjustment of temperature. This library can't run on Intel Galileo Gen 1.
url=https://kei-sakaki.jp/
architectures=*
dependencies=
core-dependencies=arduino (>=1.5.0)

keywords.txt

#######################################
# Datatypes (KEYWORD1)
#######################################

HC_SR04 KEYWORD1

#######################################
# Methods and Functions (KEYWORD2)
#######################################

setDurationRange        KEYWORD2
getDistance     KEYWORD2
getLastDistance KEYWORD2
setTemperature  KEYWORD2
getTemperature  KEYWORD2
setSoundSpeed   KEYWORD2
getSoundSpeed   KEYWORD2

※実際の文字区切りは空白ではなくタブです。

関数(メソッド)の説明

簡単にHC_SR04クラスのメソッドについて説明します。詳しくはソース・コードをご覧ください。

HC_SR04(int TirggerPin, int EchoPin)

操作対象となるHC-SR04が接続されているピン番号を指定します。TiggerPinにTrigに接続されているピン番号、EchoPinにEchoに接続されているピン番号を渡します。温度は15℃であることを前提とする初期化を行います。※6

~HC_SR04()

派生クラスを作成する時のために仮想デストラクターとして中身を空で作成してあります。

void setDurationRange(int max = MAX_ECHO_DURATION, int min = MIN_ECHO_DURATION)

計測時に許容するエコー間隔の幅をμs単位で指定します。特に値の制限はしていませんので、どのような値でも受け入れますが、常識的な値が指定されることを想定しています。maxには許容する最大値、minには許容する最小値を渡します。この範囲外の値は計算対象とせずに非数を返すようになります。センサーを特定の範囲内に障害物があるかどうかの判定を行う場合にも、この関数による指定は有用です。

float getDistance(bool inch = false)

HC-SR04を駆動して障害物までの距離を取得します。デフォルトの単位はcmですが、inchtrueを渡すとinchになります。計測範囲外の場合にはNAN(非数)を返します。

float getLastDistance(bool inch = false)

最後にHC-SR04を駆動して障害物までの距離を取得した結果を返します。デフォルトの単位はcmですが、inchtrueを渡すとinchになります。計測範囲外および計測実行前の場合にはNAN(非数)を返します。

この関数(メソッド)は、距離の計測に比較的時間がかかることから計測を行わずに前回の値を取得できるように利便性のために用意したものです。

void setTemperature(float Temperature)

HC-SR04を駆動して障害物までの距離を計測する際に想定する音速を計算するための温度を指定します。Temperatureには℃単位の想定温度を渡します。

float getTemperature()

setTemperature()で最後に指定した値を返します。デフォルトでは15.0f(15℃)を返します。

void setSoundSpeed(float SoundSpeed)

HC-SR04を駆動して障害物までの距離を計測する際に想定する音速を指定します。SoundSpeedにはメートル/秒(秒速メートル)単位の想定音速を渡します。

float getSoundSpeed()

HC-SR04を駆動して障害物までの距離を計測する際に想定する音速を返します。単位はメートル/秒(秒速メートル)で、setTemperature()あるいはsetSoundSpeed()を呼び出すと変更されます。

HC-SR04用「Arduino ライブラリ」の配布

仕様上の制限

  • HC-SR04は、TrigをHレベルにしてから、次回再度Hレベルにするまでに60ms以上あけるように規定されています。しかし、この「ライブラリ」では次回測定までに60msの間をあけたかどうかを判定していません。呼び出すスケッチ側で60ms以内に再度呼び出すことがないように調整が必要です。
  • Intel Galileo Gen 1ではボード側の仕様の問題で動作しません。
  • 測定範囲内の空気の温度が一定であることを前提としています。こればかりはどうしようもない仕様上の制限でしょう。
  • 先日と同じでキーワードの強調が安定して効きません。効いたり効かなかったりしますが、深く調べていません。

使用条件

  • このソフトウェアは現状のままで提供される、一切保証のないソフトウェアです。作成者および権利者は一切の責任を負いません。これに同意する場合に限り、本ソフトウェアを使用することができます。
    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

ダウンロード

  • HC-SR04 Supersonic Distance Sensor Library : HC_SR04.zip

温度センサーAnalog Devices ADT7410と組み合わせて使用する例

超音波センサーHC-SR04で距離を取得する際に温度で補正することができるようになったので、先日作成した温度センサーAnalog Devices ADT7410用の「ライブラリ」と組み合わせて、より正確な計測を行うスケッチを作成してみました:

//////////////////////////////////////////////////////////
// Sketch library for HC-SR04 (manufacturer unknown)
// Example Sketch - 2.
// Programmed by KEI SAKAKI. https://kei-sakaki.jp/

#include <Wire.h>
#include <adt7410.h>
#include <hc_sr04.h>

HC_SR04* hc_src04;
ADT7410* adt7410;

void setup() {
  Serial.begin(115200);

  hc_src04 = new HC_SR04(8, 9);
  adt7410 = new ADT7410();
}

void loop() {
  float temperature = adt7410->readTemperature(true);
  hc_src04->setTemperature(temperature);
  Serial.print("Temperature : ");
  Serial.print(temperature, 4);
  Serial.print(" C | Sonic Speed : ");
  Serial.print(hc_src04->getSoundSpeed(), 4);
  Serial.print(" m/s | Distance : ");
  Serial.print(hc_src04->getDistance(), 4);
  Serial.println(" cm");
  delay(1000);
}

実行すると「シリアルモニタ」に以下のような出力が1秒に1回行われていることが確認できます:

Temperature : 26.1719 C | Sonic Speed : 346.9700 m/s | Distance : 12.9593 cm

これで温度センサーAnalog Devices ADT7410で測定した温度を元に音速を計算して超音波距離センサーHC-SR04で比較的正確に距離を得ることができます。

まとめ

超音波距離センサーHC-SR04を使用して距離を測定するArduino IDE用の「ライブラリ」を作成するところまで紹介してみました。また、前回「ライブラリ」にまとめたAnalog Devicesの温度センサーADT7410と組み合わせることで、より正確な距離が得られるという例も出しました。このように「ライブラリ」にまとめることにより、スケッチに直接デバイスの制御コードを書くよりも効率的に各種デバイス(センサー)を扱うことができるということが伝わればよいなぁ、と思っていますがいかがでしょうか?

今回は温度を取り入れましたが、さらに高精度にするためには湿度や気圧も考慮する必要があります。興味があるようでしたら、そちらも組み込むように改良してみてもよいのではないかと思います。

なお、このページで配布している「ライブラリ」は、Intel Edison/Galileoでしか動作確認を行っていませんが、他のArduino IDE 1.5以降採用のボードであれば使用できる可能性は高いでしょう。

参考資料

このメモを作成するに当たり、以下のページを参考にしました:

関連記事


  • μs = マイクロ秒
  • 色は分けておいた方が見やすいので、ジャンパー・ワイヤーは色別に各種用意しておくのがお勧めです。
  • 計算にはExcel 2010を使用し、式は「=20.055*POWER(対象温度+273.15,1/2)」としました。
  • 温度の範囲は関東地方で一般に考慮が必要と思われる範囲ということで10℃~35℃までにしてみましたが、実用を考えるとさらに幅広い温度を考慮することになるでしょう。
  • 温度は音速に変換して補正情報とするので計算しますが、逆は距離の計測に不要なのでしていないということです。
  • 一般的に使用する音速340m/sは15℃環境での値なので、そこに合わせるために15℃をデフォルトとしています。

コメントを残す

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