知的好奇心 for IoT

IoT関連の知的好奇心を探求するブログです

IoT化したTAMIYAのロボットに追っかけ機能を追加した

夏なのでホラー企画として、IoT化したTAMIYAのロボットに追っかけ機能を追加してみました。レーザー測距センサモジュールで捕捉した相手にずずずんっと迫って来ます!

f:id:IntellectualCuriosity:20180725223131j:plain

真ん中のがレーザー測距モジュールです。

でも、レーザーは肉眼で見ても平気な出力のものだし、20cmまで近づくと止まるようにしていて、10cm以下になると後ずさります!

 

とりあえずビデオを上げます!

 

距離センサー

今回使用した距離センサーはレーザー方式ですが、一般によく知られているのは超音波方式のものだと思います。ぼくも最初は超音波方式のUS-015を使ったのですが、超音波方式のセンサーは対象物の素材や角度による影響が大きく、汎用性に乏しいことがわかったため使うのを止めました。

レーザー方式のVL53L0XはUS-015の倍くらいの値段ですが、それでも1,000円程度なので、距離を測りたい対象物が決まったものでなかったり、角度が斜めになったりする場合はレーザー方式の距離センサーを選んだ方がいいと思います。

レーザー方式の弱点は肉眼で見ても安全な出力にすると、計れる距離が短くなることですね。VL53L0Xは2メートル程度しか測ることができません。US-015はVL53L0Xの半額ですが測れる距離は倍の4メートルなのでお得です。使用状況に応じて使い分けるといいと思います。

f:id:IntellectualCuriosity:20180729032023j:plainf:id:IntellectualCuriosity:20180729032045j:plain
VL53L0XUS-015

VL53L0Xの接続

VL53L0Xには付属のコネクタ付きケーブルが付いていて、このようなピン配置になっています。付属マニュアルによると5:XSHUTと6:GPIO1は繋がなくても動作すると書いてあったので、繋いでいません。I2CインターフェースのSDAとSCLはESP8266のデフォルトのGPIO4とGPIO5に繋いで、V+を3.3VにGNDはGNDに繋いでいます。

f:id:IntellectualCuriosity:20180811191224p:plain

 

回路図

f:id:IntellectualCuriosity:20180812234732p:plain

f:id:IntellectualCuriosity:20180812234756p:plain

バッテリーはニッケル水素電池3本で3.6Vで動作させています。

距離センサーは秋月電子製のfritzing素材が無かったので、Adafruit製のもので代用しています。

 

BLYNK

実行画面

f:id:IntellectualCuriosity:20180815223350p:plain

FOLLOW:追っかけ機能ON/OFFスイッチ

DISTANCE X10CM:追っかけ機能有効距離。X10なので10だと100cm。

JOYSTICK:マニュアル操作用のジョイスティック

FOLLOW Button設定画面

f:id:IntellectualCuriosity:20180815224159p:plain

DISTANCE Slider設定画面

f:id:IntellectualCuriosity:20180815224351p:plain

Joystick設定画面

f:id:IntellectualCuriosity:20180815224054p:plain

 

スケッチ

もったいぶっていた訳ではありませんが、お待ちかねのスケッチです。

※2019/5/21 追っかけモードをOFFにしたときにモーターを止めるように修正

/*
 * Robot.ino (with follow function)
 * May 21, 2019
 * By Hiroyuki ITO
 * http://intellectualcuriosity.hatenablog.com/  
 * MIT Licensed.
*/
#define BLYNK_PRINT Serial

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <Wire.h>
#include <Adafruit_VL53L0X.h>

// Set Blynk Auth Token and WiFi credentials.
const char *auth = "YourAuthToken";
const char *ssid = "YourNetworkName";
const char *pass = "YourPassword";

// DRV8835
// GPIO
#define PIN_AIN1 14  // D5 Right Motor Blue
#define PIN_AIN2 12  // D6 Right Motor Yellow
#define PIN_BIN1 13  // D7 Left  Motor Yellow
#define PIN_BIN2 15  // D8 Left  Motor Blue
// PWM
#define PWM_FREQ 1000 // PWM frequency: 1000Hz(1kHz)
#define PWM_RANGE 100 // PWM range: 100

// LED GPIO
#define PIN_EYER 0    // D3 Right LED
#define PIN_EYEL 16   // D0 Left  LED

// VL53LOX
Adafruit_VL53L0X lox = Adafruit_VL53L0X();

// Follow me mode
#define FARAWAY   0
#define FORWARD   1
#define REVERSE   2
#define STOP      3

// Global
int distance_mode = STOP;
int mr_pwm = 0;       // Right motor speed
int ml_pwm = 0;       // Left  motor speed
int follow;
int distance_limit;
BlynkTimer timer;
VL53L0X_RangingMeasurementData_t measure;

/*
 * ABS Function
 */
int setABS(int pwm_value) {
  pwn_value = abs(pwm_value);
  if (pwm_value> 100) {
    pwm_value= 100;
  }
  return pwm_value;
}

/*
 * Set Motor
 */
void setMotor() {
  // Right Motor
  if (mr_pwm > -1) {
    // CW Forward
    analogWrite(PIN_AIN1, 0);
    analogWrite(PIN_AIN2, setABS(mr_pwm));
  } else {
    // CCW Reverse
    analogWrite(PIN_AIN1, setABS(mr_pwm));
    analogWrite(PIN_AIN2, 0);
  }

  // Left Motor
  if (ml_pwm > -1) {
    // CW Forward
    analogWrite(PIN_BIN1, 0);
    analogWrite(PIN_BIN2, setABS(ml_pwm));
  } else {
    // CCW Reverse
    analogWrite(PIN_BIN1, setABS(ml_pwm));
    analogWrite(PIN_BIN2, 0);
  }
}

/*
 * Keep Distance
 */
void keepDistance() {
  if (!follow) {
    return;
  }
  
  lox.rangingTest(&measure, false); // pass in 'true' to get debug data printout!
  int distance = measure.RangeMilliMeter;
  BLYNK_LOG2("distance: ", distance);

  if (distance < 100) {             // Less than 10cm
    // Reverse
    distance_mode = REVERSE;
    mr_pwm = -100;
    ml_pwm = -100;
    digitalWrite(PIN_EYEL, HIGH);
    digitalWrite(PIN_EYER, HIGH);
    BLYNK_LOG("keepDistance: REVERSE");
  } else if (distance < 200) {     // 10cm - 20cm
    // Stop
    distance_mode = STOP;
    mr_pwm = 0;
    ml_pwm = 0;
    digitalWrite(PIN_EYEL, HIGH);
    digitalWrite(PIN_EYER, HIGH);
    BLYNK_LOG("keepDistance: STOP");
  } else if (distance < distance_limit) { // 20cm - Limit
    // Forward
    distance_mode = FORWARD;
    mr_pwm = 100;
    ml_pwm = 100;
    digitalWrite(PIN_EYEL, HIGH);
    digitalWrite(PIN_EYER, HIGH);
    BLYNK_LOG("keepDistance: FORWARD");
  } else {                          // Greater than 100cm
    // FarAway
    if (distance_mode != FARAWAY) {
      mr_pwm = 0;
      ml_pwm = 0;
    }
    distance_mode = FARAWAY;
    digitalWrite(PIN_EYEL, LOW);
    digitalWrite(PIN_EYER, LOW);
    BLYNK_LOG("keepDistance: FARAWAY");
  }
}

/*
 * Receive Data from Cloud at connected
 */
BLYNK_CONNECTED() {
  Blynk.syncAll();
}

/*
 * Follow Button
 */
BLYNK_WRITE(V2) {
  follow = param.asInt();
  if (!follow) {
    digitalWrite(PIN_EYEL, LOW);
    digitalWrite(PIN_EYER, LOW);
    mr_pwm = 0;       // Right motor stop
    ml_pwm = 0;       // Left  motor stop
  }
  BLYNK_LOG2("Follow Button:", follow);
}

/*
 * Distance Slider
 */
BLYNK_WRITE(V3) {
  distance_limit = param.asInt() * 100;   // mm
  BLYNK_LOG2("Distance Slider:", distance_limit);
}

/*
 * Joystick x axis, y axis
 */
BLYNK_WRITE(V1) {
  if (follow && (distance_mode != FARAWAY)) {
    return;
  }

  int x = param[0].asInt(); // Joystick x axis
  int y = param[1].asInt(); // Joystick y axis
  BLYNK_LOG2("Joystick x:", x);
  BLYNK_LOG2("Joystick y:", y);

  // Forward or Reverse ?
  if (y > -1 ) {
    mr_pwm = y - x; 
    ml_pwm = y + x;
  } else {
    mr_pwm = y + x; 
    ml_pwm = y - x;
  }
  setMotor();
}

void setup() {
  delay(10);
  Serial.begin(74880);

  // Initialize GPIO
  pinMode(PIN_AIN1, OUTPUT);
  pinMode(PIN_AIN2, OUTPUT);
  pinMode(PIN_BIN1, OUTPUT);
  pinMode(PIN_BIN2, OUTPUT);
  pinMode(PIN_EYER, OUTPUT);
  pinMode(PIN_EYEL, OUTPUT);

  // Initialize PWM
  analogWriteFreq(PWM_FREQ);
  analogWriteRange(PWM_RANGE);

  // Initialize VL53LOX
  lox.begin();

  // Connect WiFi and Blynk cloud server
  Blynk.begin(auth, ssid, pass);
  timer.setInterval(200L, keepDistance); // 200 msec
}

void loop() {
  Blynk.run();
  timer.run();
  setMotor();
}

追っかけボタンをオンにすると、新設のkeepDistance()ファンクションが有効になり、#defineで定義している4つの"Follow me mode"のモードに従って、動作を切り替えるようにしています。

コードはかなりシンプルで、全体で200行程度しかないので、興味が湧いたら実際に試してみることをお勧めします。

もっと大きなロボットで動作させる前に、プロトタイプの参考として使ってください!

 

ライブラリ

おっと、忘れていました。VL53L0Xを使うのにAdafruitのライブラリを使っています。ライブラリマネージャで追加しておいてください。

f:id:IntellectualCuriosity:20180817110303p:plain

Blynkのライブラリも当然使っているので、Blynkを初めて使う人は一緒に追加してくださいね。

 

終わり