M5Stackでセンサを使って自動運転と自動回避機能を搭載
第1回:小型Arduino互換機(M5Stack)で電子工作を楽しむ
第2回:M5Stackでモータドライバを使う
こんにちは、ヨシケンです!
小型Arduino互換機のM5Stackを使ってリモコンカーを作る本連載。今回は、M5Stackにさまざまなセンサを付けて、自動で制御できるようにします。M5Stackには多くの端子が付いているので、近接センサや距離センサを挿すだけで機能を簡単に追加することができます。


M5Stackに距離センサと近接センサを付けて自動運転
今回の記事の流れ
今回の記事で必要な物一覧
M5Stack Core
ESP32を搭載し、ディスプレイ、センサなども入ったArduino互換機

ローム照度・近接センサ (RPR-0521RS)
赤外線を発しその反射光を測定して、光の強度や近接度を計測します。

超音波距離センサ (HC-SR04)
超音波を発し、その跳ね返りにより距離を測定するモジュール

Grove I2C モータドライバ
I2Cという通信方式で、Grove端子で接続して使えるモータドライバ

9V電池と電池ボックス

小型モーター、車輪など

1. 照度・近接センサの設定
リモコンカーが走る際、障害物に当たりそうになったり、机の上から落ちそうになったら、それを自動的に回避できるようにしたいと思います。
まず、落ちそうになるかどうかを判別するために近接センサを使用します。近接センサとしては、ロームセンサ評価キットの照度・近接一体型センサモジュール(RPR-0521RS)を使ってみます。

このセンサは、赤外線LEDを発し、その反射光を検知することにより、物体などの近接度を測ります。また照度センサにより明るさも同時に測定します。
このセンサを使うために、以下ローム・センサのページに行き、Arduino用のライブラリをダウンロードします。
https://www.rohm.co.jp/sensor-shield-support/ps-als-sensor
サイト中にあるzipファイルをダウンロードし、Arduino IDEのライブラリをインクルード メニューからインストールします。

次にM5Stackとセンサをつないでみます。M5Stackには上下に8本、左右に15本の端子が付いており、裏面を見るとそのピン配置が書かれています。ArduinoのGPIO(入出力ピン)は数字で書かれたピン番号と対応しています。

M5Stack裏面のPin配置図
照度・近接センサはI2Cという通信方式を使います。I2C通信を行うためにM5Stackの下部端子のうち、SDA=G21とSCL=G22を使います。以下、表に従ってセンサとM5Stackをつないでください。
| センサー側端子 (右から) |
M5Stack側 (下部) |
| VDO | 3.3V (オレンジ) |
| GND | GND (青) |
| SDA | GPIO21 (黄) |
| SCL | GPIO22 (緑) |
| INT | NA |

センサとM5Stackをつないだら、Arduino IDEのスケッチ例のところから、先ほどインストールしたサンプルのPRP-0521RSを選びます。
サンプルに追加してM5Stackの画面表示などをするので、M5Stack_PRP-0521RS.inoとして別名保存します。そこに以下の様なプログラムを追加します(1、11〜14、20、29〜30、36、39行目)。
<M5Stack.h>は、M5Stack用のライブラリです。Wire.begin(21,22)は、M5StackでI2Cを使うための宣言になります。
[M5Stack_PRP-0521RS.ino]
#include <M5Stack.h>
#include <Wire.h>
#include <RPR-0521RS.h>
RPR0521RS rpr0521rs;
void setup() {
byte rc;
Serial.begin(115200);
while (!Serial);
Wire.begin(21,22);
M5.begin();
M5.Lcd.setTextSize(2);
M5.Lcd.setRotation(3);
rc = rpr0521rs.init();
}
void loop() {
Wire.begin(21,22);
byte rc;
unsigned short ps_val;
float als_val;
byte near_far;
rc = rpr0521rs.get_psalsval(&ps_val, &als_val);
if (rc == 0) {
Serial.print(F("RPR-0521RS (Proximity) = "));
M5.Lcd.setCursor(10, 20);
M5.Lcd.printf("RPR-0521RS: ");
Serial.print(ps_val);
Serial.print(F(" [count]"));
near_far = rpr0521rs.check_near_far(ps_val);
if (near_far == RPR0521RS_NEAR_VAL) {
Serial.println(F(" Near"));
M5.Lcd.printf("M5 Near!");
} else {
Serial.println(F(" Far"));
M5.Lcd.printf("M5 Far !");
}
}
delay(500);
}
このプログラムをM5Stackで使う際、ひとつ変更が必要です。PRP-0521RS.cppというファイルを開いて、#include 部の”avr/”を取り除きます(通常、インストールしたライブラリは、Arduino > Libraries > RPR-0521RS 内にあります)。こうすることで、M5Stackでpgmspace.hを正常に読み取ることができるようになります。
[ PRP-0521RS.cppの場所、変更前。赤枠部分から”avr/”を取り除く。 ]

この変更が済んだら、M5Stack_PRP-0521RS.inoを実行します。自動的に計測を始め、写真のように指を近づけると、画面上にNear!というメッセージが出るようになります。

指を近づけた時、近くにものがある”Near”が出力される
2. 距離センサの設定
次に、M5Stackに距離センサを付けて障害物を検知し、それを回避するようにします。こちらのHC-SR04という超音波距離センサを使います。超音波を発し、その跳ね返りにより距離を測定するモジュールです。

この超音波センサでは、以下表、写真のように接続をおこないます。
| センサー側端子 (左から) |
M5Stack側 (左側側面) |
| VCC | 5V (赤) |
| Trig | GPIO2 (オレンジ) |
| Echo | GPIO5 (黄) |
| GND | GND (黒) |

この距離センサを動かすプログラムM5Stack_Dist.inoはこちらです。出力のTRIGにG2、入力となるECHOにG5の接続をおこなったので、それぞれGPIO番号を指定しています。
[M5Stack_Dist.ino]
#include <M5Stack.h>
#define TRIG 2
#define ECHO 5
int times;
int distance;
void setup() {
Serial.begin(115200);
while (!Serial);
pinMode(TRIG, OUTPUT);
pinMode(ECHO, INPUT);
M5.begin();
M5.Lcd.setTextSize(2);
M5.Lcd.setRotation(3);
}
void loop() {
// 超音波を発生させる
digitalWrite(TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG, LOW);
// 超音波を受け取る
times = pulseIn(ECHO, HIGH);
distance = (int)(times * 0.017);
// モニター、ディスプレイに表示させる
Serial.println(distance);
M5.Lcd.setCursor(0, 0);
M5.Lcd.print(distance);
delay(500);
}
このプログラムを流して、距離センサに指を近づけてみると、その距離を測ってくれます。シリアルモニタとM5Stackの画面に手などかざした物との距離を表示するようになると思います。

シリアルモニタ(左側)とM5Stack画面(右側)に計測した距離を表示
3. センサで測定しながら自動運転
最後にセンサの結果に基づいて、障害物を回避したり、机から落ちないように制御を加えます。まずセンサと共にモータドライバ、車輪などを再度接続します。モータドライバはGrove端子に、距離センサは前方を捉えやすい場所に、近接センサは下方にセットします。

センサとモータを連動させたプログラムを作ります。プログラムの中では距離センサで距離が5cm以下になったら、障害物が近いと判断します。また、近接センサで床との近さを測り、これがFarとなった時、机の端から落ちそうだと判断します。この際に、モータを反転させてその障害物を回避します。それ以外は前に進み続けます。
サンプルプログラムM5Stack_Near_Dist_Motor.inoはこのようになりました。
#include <M5Stack.h>
#include <Wire.h>
#include <RPR-0521RS.h>
RPR0521RS rpr0521rs;
#define TRIG 2
#define ECHO 5
int times;
int distance;
#include "Grove_I2C_Motor_Driver.h"
#define I2C_ADDRESS 0x0f
void setup() {
Serial.begin(115200);
while (!Serial);
byte rc;
rc = rpr0521rs.init();
pinMode(TRIG, OUTPUT);
pinMode(ECHO, INPUT);
Motor.begin(I2C_ADDRESS);
Wire.begin(21,22);
M5.begin();
M5.Lcd.setTextSize(4);
M5.Lcd.setCursor(10,0);
M5.lcd.print("M5 Near Dist Motor");
}
void loop() {
Wire.begin(21,22);
byte rc;
unsigned short ps_val;
float als_val;
byte near_far;
M5.lcd.clear();
M5.Lcd.setCursor(10,0);
M5.lcd.print("M5 Near Dist Motor");
M5.Lcd.setCursor(10,50);
rc = rpr0521rs.get_psalsval(&ps_val, &als_val);
if (rc == 0) {
Serial.print(F("RPR-0521RS (Proximity) = "));
Serial.print(ps_val);
Serial.print(F(" [count]"));
near_far = rpr0521rs.check_near_far(ps_val);
if (near_far == RPR0521RS_NEAR_VAL) {
Serial.println(F(" Near"));
M5.lcd.print("Near!");
} else {
Serial.println(F(" Far"));
M5.lcd.print("Far!");
}
}
// 超音波を発生させる
digitalWrite(TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG, LOW);
// 超音波を受け取る
times = pulseIn(ECHO, HIGH);
distance = (int)(times * 0.017);
// ディスプレイに表示させる
Serial.println(distance);
M5.Lcd.setCursor(10, 100);
M5.Lcd.print(distance);
M5.Lcd.setCursor(10,150);
// 距離が6より小さくなったら、近接度が大きくなったら、モーターを反転
if ( ((distance > 0) and (distance < 6)) or (near_far != RPR0521RS_NEAR_VAL) ) {
Motor.speed(MOTOR1, 100);
Motor.speed(MOTOR2, -100);
delay(800);
Motor.speed(MOTOR1, 100);
Motor.speed(MOTOR2, 100);
M5.lcd.print("Danger!");
delay(800);
} else {
// 障害物がなければ前に進む
Motor.speed(MOTOR1, -100);
Motor.speed(MOTOR2, 100);
M5.lcd.print("Forward!");
}
delay(300);
}
プログラムができたら、これをM5Stackに流し込みます。電源を入れると車輪が回り始め、前に進みます。障害物や机から落ちそうになったら、反転するかどうか確かめてください。

動いている動画はこちらになります。
4. まとめ
今回の連載では、M5Stackをリモコン/自動カーにするための工作を進めています。第3回では、前方向の物体を確認するために距離センサを、そして下の状況をチェックするために近接センサを使用しました。
その二つのセンサとモータの回転を連動させて自動運転させるようにしましたが、上手く動いたでしょうか?どれくらい障害物に近づいたのかであったり、反転の時間などは微調整が必要ですので、使いながら数値を変えて試してみてください。
M5Stackにはまだ使える端子があるので、他のセンサを追加して計測しながら動かすのも面白いかもしれません。室内温度や空気清浄度を測りながら自動で回遊するようなアイデアもあります。
次回はスマホなど外部からリモコンカーをコントロールできるようにします。お楽しみに!
今回の連載の流れ
第1回:小型Arduino互換機(M5Stack)で電子工作を楽しむ
第2回:M5Stackでモータドライバを使う
第3回:M5Stackでセンサを使って自動運転と自動回避機能を搭載(今回)
第4回:M5Stackをスマホと連動させてコントロールする
