M5Stackで始めるセンサ・インターフェーシング (7) 超音波距離センサを利用

 電子工作で距離を測るのに最も使われているのが、超音波距離センサです。丸っこい超音波センサ(マイクとスピーカ)を二つ使うので、ロボットの目のようでとても雰囲気があります。市販品を購入すると、ほとんど同じ外観でも複数あることがわかりました。

  • 信号 Trig、Echo、OUT(写真上。アマゾンで入手)
  • 信号 Trig、Echo
  • 信号 SIG(写真下。Grove - Ultrasonic Ranger V2.0)
  • 電圧 5V専用
  • 電圧 3.3/5V両方可

 超音波とは、すごい音波のことではなく周波数が40kHz付近のことを指します。人が発する音声は2~5kHz、聞こえるのは20kHzまでが多いようです。コウモリは、キャリア周波数が数十kHzから100kHzの超音波パルスを発するそうです。メガネの洗浄器にもこの40kHzが使われます。歯石除去器の超音波スケーラにも使われます。

 片方の超音波スピーカから超音波を出し、もう一方の超音波マイクで受信する仕組みなので、二つの目玉のように見えます。

 音波を発してから物体に反射し、戻ってきた音波を受信する時間を測って距離を得るのは、ToF(Time of Flight)と呼ばれています。最近の距離センサは光(レーザー)が主流で、この連載の1回目でVL53L1Xを使いました。

Grove - Ultrasonic Ranger V2.0のおもなスペック

 Seeedの製品Grove - Ultrasonic Ranger V2.0は、電源以外に信号はSigのみです。Groveコネクタは4ピンで、1ピンは何もつながっていないNCです。

  • 動作電圧 3.2~5.2V
  • 消費電流 8mA
  • 周波数 40kHz
  • 解像度 1cm
  • 出力 PWM
  • 測定範囲 15度
  • トリガ・パルス 10us

接続

Ultrasonic Ranger
Groveコネクタ

M5Stack
GND GND
Vcc 3V3
NC -
SIG 21番

ライブラリの導入

 ライブラリ管理の検索欄にultrasonicと入力して、たくさん表示された中からGrove Ultrasonic Rangerをインストールします。

 スケッチ例からUltrasonicDisplayOnTermを読み込みます。

 デフォルトでは、7番ピンにSIGピンをつなぐ記述なので、

  Ultrasonic ultrasonic(21);

と21番に修正し、インチ表示部分をコメントアウトしてからコンパイル、実行します。センサの前に手のひらをかざしてみたら、正しいそうな値を表示しています。

波形

 センサの前25cmぐらいに手をかざしたとき。

 155cmぐらいのとき。

 パルスを出してから戻ってくるまで8.997msかかっています。往復なので半分にします。音速は温度(20℃とした)の関数なので、

  8.997 / 2 * (331.5 + 0.6 * 20) / 1000000 * 100 * 1000 = 154.5[cm]

 シリアルモニタの表示は155もしくは154cmでした。

スケッチを独自に作る

 21番を出力にして10usのパルスを発信します。すぐに入力に切り替え、LOWになる時間をpulseIn()で読み取ります。

 実行時の波形です。ライブラリを利用したときと同じです。

 スケッチです。音速は340m/secで近似しています。

const int SigPin = 21;
long duration;
int distance;

void setup() {
Serial.begin(9600);
}

void loop() {
pinMode(SigPin, OUTPUT);
digitalWrite(SigPin, LOW);
delayMicroseconds(2);
digitalWrite(SigPin, HIGH);
delayMicroseconds(10);
digitalWrite(SigPin, LOW);

pinMode(SigPin, INPUT);
duration = pulseIn(SigPin, HIGH);
distance= duration*0.034/2;
Serial.println("Distance: " + String(distance) + " cm");
delay(200);
}

三つの信号Trig、Echo、OUTがあるモデル

 この超音波距離センサはArduino用として販売されているので、動作電圧は5Vです。Trigが発振用パルスを出す端子で、Echoが受信用の端子と思われます。オシロスコープで波形を観測します。Echo端子は5V出力です。M5StackのGPIOは3.3V用なので、直接つないではいけません。

 OUT信号は変化しなかったので、何に使うか不明です。

 信号のレベル変換は専用の変換回路が望ましいです。GPIOの入力抵抗値が不明ですが、抵抗で分圧して実験的に下記の抵抗値で必要なHIGHとLOWの電圧を得られました。

Ultrasonic Ranger
Groveコネクタ

M5Stack
Vcc 5V
Trig 21番
Echo 抵抗分割回路を通して22番
OUT -
GND GND

 スケッチです。

const int trigPin = 21;
const int echoPin = 22;
const int OutPin = 23;
long duration;
int distance;

void setup() {
Serial.begin(9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}

void loop() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

duration = pulseIn(echoPin, HIGH);
distance= duration*0.034/2;
Serial.println("Distance: " + String(distance) + " cm");
delay(200);
}

 実行中の様子です。

GUIで見やすく

 Grove - Ultrasonic Ranger V2.0を使って距離を棒グラフで表示します。常に最新のデータを表示し、過去のデータも見れるように、横スクローリングをします。
 j<16のif文は画面いっぱいに16個の測定した棒グラフを描きます。
 x0>=319のif文は、右端に最新の棒グラフを描き、その間、それまで描画したグラフを左にシフトさせています。二つのルーチンで棒の色を変えていますが、特に意味はありません。

 読み取った距離データのdistanceはグローバル変数なので、readDistance()でreturnする値を使っているわけではありません。この関数内でLCDへの表示をしていますが、あまり勧められた処理ではなく、本来、loop()側で戻り値を使って表示すべきだと思います。

#include <M5Stack.h>

TFT_eSprite graph1 = TFT_eSprite(&M5.Lcd); // Sprite object graph1

int x0=0;
int j=0;
const int SigPin = 21;
long duration;
int distance;

void setup() {
Serial.begin(9600);
M5.begin();
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(YELLOW);
M5.Lcd.setCursor(10, 10);
M5.Lcd.print("Ultrasonic distance start");
graph1.setColorDepth(8);
graph1.createSprite(320, 240);
graph1.fillSprite(BLACK);
graph1.pushSprite(0, 80);
}

void loop() {
graph1.pushSprite(0, 80);
x0 += 20;

if (j<16) {
j += 1;
readDistance();
graph1.fillRect(j*18, 160-distance, 18, distance, TFT_ORANGE);
delay(1000); // wait so things do not scroll too fast
graph1.scroll(-1, 0);
}

if (x0>=319) {
x0=0;
readDistance();
graph1.fillRect(305, 160-distance, 20, distance, YELLOW);
graph1.scroll(-1, 0); // scroll graph 1 pixel left, 0 up/down
}
graph1.scroll(-1, 0);
delay(50);
}

int readDistance(){
pinMode(SigPin, OUTPUT);
digitalWrite(SigPin, LOW);
delayMicroseconds(2);
digitalWrite(SigPin, HIGH);
delayMicroseconds(10);
digitalWrite(SigPin, LOW);

pinMode(SigPin, INPUT);
duration = pulseIn(SigPin, HIGH);
distance= duration*0.034/2;
Serial.println("Distance: " + String(distance) + " cm");

M5.Lcd.fillRect(1, 30, 300, 44, BLACK);
M5.Lcd.drawRightString(String(distance), 100, 30, 4);
M5.Lcd.drawCentreString(" cm", 115, 40, 2);
return distance;
}

 実行中の様子です。最大150cmぐらいまでを正しく表示します。
 ガラスの窓はその面までの距離が測れます。黒いスピーカの筐体はあいまいな距離しか測れませんでした。

前へ

M5Stackで始めるセンサ・インターフェーシング (6) 紫外線センサVEML6070を利用