Bluetooth モジュールでRCサーボ・ESCを制御したい [第4回-操作性の向上]

2022-08-20 · Tomoki Ikegami

"文字打ち込んで操縦するのはさすがに...(笑笑)"

~ 目次 ~


1. 今回やりたいこと

 前回は MIT App Inventor を使ってラジコン操縦アプリの試作を行いました。

 前回作成したスマホアプリは、スマホキーボードから文字を打ち込んで(前進なら"F"、右折なら"R"、停止なら"N"など)操縦するというものでした(笑)。

 実際これでラジコンを操縦してみると、全然操縦できませんでした!

 今回はコントローラー側のアプリマイコン側のプログラム(ラジコンに搭載している)を改良します(^^)/

 実験に使った装置は前回と全く同じになります。

 下の画像は今の実験車を撮影したもので、色々なものがごちゃごちゃと積まれています。(笑)

実験車の様子
☝ 実験車の様子

2. 実際にやった改良の流れ

 前回までのスマホアプリとマイコンプログラムを記念すべき(?)ver.1 として、改良を進めていきます。

 改良していく度に、ver.2→3→4 としました。ソフトのリリースノートみたいな感じでメモっていきます。(そんなガチなものではございませんww)

 作ったアプリのデータは最後にまとめて載せておきます。

3. 改良の過程

3.1. ◆ ver.2 ~とりあえず文字打ち込みはやめよう~

 文字を打ち込んで操縦はさすがに難しすぎるので、ボタンで操作するようにアプリを改良します。

 改良するのはアプリ側だけで、マイコン側のプログラムはそのままです。

3.1.1. スマホアプリ

 かなり操縦はしずらいですが、ver.1よりはマシに感じました。

 スロットル(アクセル)とステア(ハンドル)の信号をごちゃ混ぜで送っている割には、まあまあ動きました(なんでだろう)

アプリ外観(デザイン編集タブ)
☝ アプリ外観(デザイン編集タブ)
アプリの中身(ブロック編集タブ)
☝ アプリの中身(ブロック編集タブ)

3.1.2. マイコンプログラム

 ver.1と全く同じです。

#include<SoftwareSerial.h>  //Bluetoothの無線通信に使用
#include <VarSpeedServo.h>  //サーボモータの制御に使用
VarSpeedServo myservo;    // create servo object to control a servo
VarSpeedServo myesc;    // create servo object to control a esc

/* ステアリングの設定 */
int mov_speed_ST = 30; //ステア移動速度
int center_pos = 90; //ステア中心位置 [サーボモータの中心位置 (90°)]
int left_DR = 20; //左位置 [中心位置より反時計回りに20°回転した位置]
int right_DR = 25; //右位置 [中心位置より時計回りに25°回転した位置]
int left_max = center_pos - left_DR; //左の最大切れ角
int right_max = center_pos + right_DR; //右の最大切れ角

/* スロットルの設定 */
int mov_speed_TH = 5; //スロットル移動速度
int brake_speed = 100; //ブレーキ速度
int neutral_pos = 91; //中立位置 [スロットルの中立位置 (90) ※ESCの設定によってずれがあるので、前後に走行しないよう値を調整する。ESC側を90で中立になるよう設定してもよい。]
int forward_DR = 20; //前進位置
int backward_DR = 20; //バック位置
int forward_max = neutral_pos + forward_DR; //前進の最大位置
int backward_max = neutral_pos - backward_DR; //バックの最大位置

//速度(mov_speed_ST,mov_speed_TH)は 1~255 の範囲で与える。(0にすると最大速度で移動)
//スロットル、サーボモータの値(pos)の範囲は、 0≦ pos ≦180 で与える。
//myservo.write 関数には回転角を絶対的な位置で与える。例) 90°から 45°反時計回りに動いてほしいときは、-45ではなく、45を関数に入力する。


SoftwareSerial Bluetooth(10, 11); // Arduinoから見て10ピンがRX(受信), 11ピンがTX(送信)になる。bluetoothモジュールのTXにはArduinoのRX(10)が、bluetoothモジュールのRXにはArduinoのTX(11)が接続されていれば良い。

void setup() {
  Bluetooth.begin(9600); //シリアルポートを開いて、伝送速度を9600[bps] に設定
  Serial.begin(9600); //シリアルモニタで確認用
  myservo.attach(9); //サーボモータのPWM端子とArduinoの9番ピンを接続
  myesc.attach(6); //ESCのPWM端子とArduinoの6番ピンを接続
}

void loop() {
  if (Bluetooth.available()) {
    char input = Bluetooth.read(); //受信したテキストを変数inputに保存
    Serial.println(input); //受信したテキストをシリアルモニタに表示
    if (input == 'L') {
      myservo.write(left_max, mov_speed_ST, true); // ステアを左(Left)に切る
    } else if (input == 'C') {
      myservo.write(center_pos, mov_speed_ST, true); // ステアを中心(Center)に
    } else if (input == 'R') {
      myservo.write(right_max, mov_speed_ST, true);  //ステアを右(Right)に切る
    } else if (input == 'F') {
      myesc.write(forward_max, mov_speed_TH, true);  //前進(Forward)
    } else if (input == 'N') {
      myesc.write(neutral_pos, brake_speed, true);  //中立(Neutral)
    } else if (input == 'B') {
      myesc.write(backward_max, mov_speed_TH, true);  //後退(Backward)
    }
    else {
    }
  }
}

3.2. ◆ ver.3 ~片手で操作したいな~

 スマホを使って、片手でラジコン操作したらカッコイイと思いました!(そうは思わないですかね…)

 そこで、前進、後進、左折、右折 ボタンを親指が届く範囲にまとめます。あと、前進しながら左折するなどの動作があるのでそのようなボタンも追加します。

 色々やってみましたが、正直あまり上手く動きませんでした。(トホホ…)

3.2.1. スマホアプリ

 アプリを作る前に、考えられる動作パターン(止まりながら左折、前進しながら右折など)を1つずつ書き出していきます。(地道な作業)

 全パターンを書き出したのが下のパラパラ漫画です。(速すぎて見えねぇよ…)

考えられる全動作パターン
☝ 考えられる全動作パターン

 上の画像では分かりずらいと思うので、1枚の画像に全動作パターンをまとめます。(下図参照)

 文字が小さくて読みずらいと思うので、必要に応じて拡大しながら見てください。それぞれのパターンに A~I のアルファベットを振りました。

 A~I の動きができれば、車は前後左右に動くことができます。いわゆるフルファンクションというやつです。(おもちゃ売り場とかで見たことありませんか?)

 停止と書いている部分は、実車で言うところのニュートラル(駆動していない状態)と考えたほうが良いかもしれません。ここでは、クローララジコン用ESCを使って実験しているので、ニュートラル状態になったとき自動的にブレーキがかかります。

考えられる全動作パターン
☝ 考えられる全動作パターン

 この画像を参考にして作成したアプリは、下の画像のようになります。

 作ってから気づいたのですが、4つ角のボタンは全く意味がありません。機能の割当を忘れてました( ;∀;)

 大まかな動作原理としては、押されたボタンの組み合わせから A~I のアルファベットを生成し、マイコンに送る仕組みです。

 下のブロックでCH1はステアの操作、CH2はスロットルの操作になります。(CH1→0:中立,1:左ステア,-1:右ステア CH2→0:中立,1:前進,-1:後進)

 ブロック一番下のタイマーは信号の送信に使っていて、100ms ごとにデータを送信しています。

アプリ外観(デザイン編集タブ)
☝ アプリ外観(デザイン編集タブ)
アプリの中身(ブロック編集タブ)
☝ アプリの中身(ブロック編集タブ)

3.2.2. マイコンプログラム

 入力された文字に応じて、動作を決めるという点はver.1,ver.2と同じです。

 入力信号に変化があったときだけラジコンを動かすように、flag, old_input という変数を追加しています。

#include<SoftwareSerial.h>  //Bluetoothの無線通信に使用
#include <VarSpeedServo.h>  //サーボモータの制御に使用
VarSpeedServo myservo;    // create servo object to control a servo
VarSpeedServo myesc;    // create servo object to control a esc

/* ステアリングの設定 */
int mov_speed_ST = 30; //ステア移動速度
int center_pos = 90; //ステア中心位置 [サーボモータの中心位置 (90°)]
int left_DR = 20; //左位置 [中心位置より反時計回りに20°回転した位置]
int right_DR = 25; //右位置 [中心位置より時計回りに25°回転した位置]
int left_max = center_pos - left_DR; //左の最大切れ角
int right_max = center_pos + right_DR; //右の最大切れ角

/* スロットルの設定 */
int mov_speed_TH = 5; //スロットル移動速度
int brake_speed = 100; //ブレーキ速度
int neutral_pos = 91; //中立位置 [スロットルの中立位置 (90) ※ESCの設定によってずれがあるので、前後に走行しないよう値を調整する。ESC側を90で中立になるよう設定してもよい。]
int forward_DR = 20; //前進位置
int backward_DR = 20; //バック位置
int forward_max = neutral_pos + forward_DR; //前進の最大位置
int backward_max = neutral_pos - backward_DR; //バックの最大位置

//速度(mov_speed_ST,mov_speed_TH)は 1~255 の範囲で与える。(0にすると最大速度で移動)
//スロットル、サーボモータの値(pos)の範囲は、 0≦ pos ≦180 で与える。
//myservo.write 関数には回転角を絶対的な位置で与える。例) 90°から 45°反時計回りに動いてほしいときは、-45ではなく、45を関数に入力する。

SoftwareSerial Bluetooth(10, 11); // Arduinoから見て10ピンがRX(受信), 11ピンがTX(送信)になる。bluetoothモジュールのTXにはArduinoのRX(10)が、bluetoothモジュールのRXにはArduinoのTX(11)が接続されていれば良い。

/*入力された値が変わらないときに、前の値を維持するためのフラグ*/
int flag = 0;
char old_input = 'C';

void setup() {
  Bluetooth.begin(9600); //シリアルポートを開いて、伝送速度を9600[bps] に設定
  Serial.begin(9600); //シリアルモニタで確認用
  myservo.attach(9); //サーボモータのPWM端子とArduinoの9番ピンを接続
  myesc.attach(6); //ESCのPWM端子とArduinoの6番ピンを接続
}

void loop() {
  delay(10);
  if (Bluetooth.available()) {
    char input = Bluetooth.read(); //受信したテキストを変数inputに保存
    Serial.println(input); //受信したテキストをシリアルモニタに表示

    if (input == 'A' && flag == 0) {
      old_input = input;
      myservo.write(center_pos, mov_speed_ST, true); // ステアを中心(Center)に
      myesc.write(forward_max, mov_speed_TH, true);  //前進(Forward)
      flag = 1;
    } else if (input == 'B' && flag == 0) {
      old_input = input;
      myservo.write(center_pos, mov_speed_ST, true); // ステアを中心(Center)に
      myesc.write(backward_max, mov_speed_TH, true);  //後退(Backward)
      flag = 1;

    } else if (input == 'C' && flag == 0) {
      old_input = input;
      myservo.write(center_pos, mov_speed_ST, true); // ステアを中心(Center)に
      myesc.write(neutral_pos, brake_speed, true);  //中立(Neutral)
      flag = 1;

    } else if (input == 'D' && flag == 0) {
      old_input = input;
      myservo.write(left_max, mov_speed_ST, true); // ステアを左(Left)に切る
      myesc.write(neutral_pos, brake_speed, true);  //中立(Neutral)
      flag = 1;

    } else if (input == 'E' && flag == 0) {
      old_input = input;
      myservo.write(right_max, mov_speed_ST, true);  //ステアを右(Right)に切る
      myesc.write(neutral_pos, brake_speed, true);  //中立(Neutral)
      flag = 1;

    } else if (input == 'F' && flag == 0) {
      old_input = input;
      myservo.write(left_max, mov_speed_ST, true); // ステアを左(Left)に切る
      myesc.write(forward_max, mov_speed_TH, true);  //前進(Forward)
      flag = 1;

    } else if (input == 'G' && flag == 0) {
      old_input = input;
      myservo.write(right_max, mov_speed_ST, true);  //ステアを右(Right)に切る
      myesc.write(forward_max, mov_speed_TH, true);  //前進(Forward)
      flag = 1;

    } else if (input == 'H' && flag == 0) {
      old_input = input;
      myservo.write(left_max, mov_speed_ST, true); // ステアを左(Left)に切る
      myesc.write(backward_max, mov_speed_TH, true);  //後退(Backward)
      flag = 1;

    } else if (input == 'I' && flag == 0) {
      old_input = input;
      myservo.write(right_max, mov_speed_ST, true);  //ステアを右(Right)に切る
      myesc.write(backward_max, mov_speed_TH, true);  //後退(Backward)
      flag = 1;

    }else if(input != old_input){ 
         flag = 0;
    }

  }
}

3.3. ◆ ver.4 ~ver.3の改良版~

 ver.3 よりもアプリやプログラムをシンプルにしました。ver.4では4つ角のボタンは有効になっています。

 ver.3よりはちゃんと動くようになりましたが、タイムラグが起こるという課題は解決できませんでした。(解決できたら、回を改めて解決方法を掲載したいと思います)

3.3.1. スマホアプリ

 見た目ver.3と全く同じですが、タイマーを無くしてシンプルにしました。

 中身(ブロック)に関しては、ver.1と同じような感じです。

 ver.3まではbluetoothの接続処理が不安定だったので、MIT App InventorフォーラムにあったJuan_Antonioさんの投稿1を参考に修正しました。

 ブロック右側の透明な部分(文字だけ浮いている部分)は、ブロックを無効にしている(テキストのプログラムで言うところのコメントアウト)だけです。

アプリ外観(デザイン編集タブ)
☝ アプリ外観(デザイン編集タブ)
アプリの中身(ブロック編集タブ)
☝ アプリの中身(ブロック編集タブ)

3.3.2. マイコンプログラム

 こちらもシンプルにしました。flagなどによる処理を無くしました。

#include <SoftwareSerial.h>  //Bluetoothの無線通信に使用
#include <VarSpeedServo.h>  //サーボモータの制御に使用
VarSpeedServo myservo;    // create servo object to control a servo
VarSpeedServo myesc;    // create servo object to control a esc

/* ステアリングの設定 */
int mov_speed_ST = 30; //ステア移動速度
int center_pos = 90; //ステア中心位置 [サーボモータの中心位置 (90°)]
int left_DR = 20; //左位置 [中心位置より反時計回りに20°回転した位置]
int right_DR = 25; //右位置 [中心位置より時計回りに25°回転した位置]
int left_max = center_pos - left_DR; //左の最大切れ角
int right_max = center_pos + right_DR; //右の最大切れ角

/* スロットルの設定 */
int mov_speed_TH = 5; //スロットル移動速度
int brake_speed = 100; //ブレーキ速度
int neutral_pos = 91; //中立位置 [スロットルの中立位置 (90) ※ESCの設定によってずれがあるので、前後に走行しないよう値を調整する。ESC側を90で中立になるよう設定してもよい。]
int forward_DR = 20; //前進位置
int backward_DR = 20; //バック位置
int forward_max = neutral_pos + forward_DR; //前進の最大位置
int backward_max = neutral_pos - backward_DR; //バックの最大位置

//速度(mov_speed_ST,mov_speed_TH)は 1~255 の範囲で与える。(0にすると最大速度で移動)
//スロットル、サーボモータの値(pos)の範囲は、 0≦ pos ≦180 で与える。
//myservo.write 関数には回転角を絶対的な位置で与える。例) 90°から 45°反時計回りに動いてほしいときは、-45ではなく、45を関数に入力する。

SoftwareSerial Bluetooth(10, 11); // Arduinoから見て10ピンがRX(受信), 11ピンがTX(送信)になる。bluetoothモジュールのTXにはArduinoのRX(10)が、bluetoothモジュールのRXにはArduinoのTX(11)が接続されていれば良い。

void setup() {
  Bluetooth.begin(9600); //シリアルポートを開いて、伝送速度を9600[bps] に設定
  Serial.begin(9600); //シリアルモニタで確認用
  myservo.attach(9); //サーボモータのPWM端子とArduinoの9番ピンを接続
  myesc.attach(6); //ESCのPWM端子とArduinoの6番ピンを接続
}

void loop() {

  if (Bluetooth.available()) {
    char input = Bluetooth.read(); //受信したテキストを変数inputに保存
    Serial.println(input); //受信したテキストをシリアルモニタに表示

    if (input == 'A') {
      myservo.write(center_pos, mov_speed_ST, true); // ステアを中心(Center)に
      myesc.write(forward_max, mov_speed_TH, true);  //前進(Forward)
    } else if (input == 'B') {
      myservo.write(center_pos, mov_speed_ST, true); // ステアを中心(Center)に
      myesc.write(backward_max, mov_speed_TH, true);  //後退(Backward)
    } else if (input == 'C') {
      myservo.write(center_pos, mov_speed_ST, true); // ステアを中心(Center)に
      myesc.write(neutral_pos, brake_speed, true);  //中立(Neutral)
    } else if (input == 'D') {
      myservo.write(left_max, mov_speed_ST, true); // ステアを左(Left)に切る
      myesc.write(neutral_pos, brake_speed, true);  //中立(Neutral)
    } else if (input == 'E') {
      myservo.write(right_max, mov_speed_ST, true);  //ステアを右(Right)に切る
      myesc.write(neutral_pos, brake_speed, true);  //中立(Neutral)
    } else if (input == 'F') {
      myservo.write(left_max, mov_speed_ST, true); // ステアを左(Left)に切る
      myesc.write(forward_max, mov_speed_TH, true);  //前進(Forward)
    } else if (input == 'G') {
      myservo.write(right_max, mov_speed_ST, true);  //ステアを右(Right)に切る
      myesc.write(forward_max, mov_speed_TH, true);  //前進(Forward)
    } else if (input == 'H') {
      myservo.write(left_max, mov_speed_ST, true); // ステアを左(Left)に切る
      myesc.write(backward_max, mov_speed_TH, true);  //後退(Backward)
    } else if (input == 'I') {
      myservo.write(right_max, mov_speed_ST, true);  //ステアを右(Right)に切る
      myesc.write(backward_max, mov_speed_TH, true);  //後退(Backward)
    } else {
    }

  }
}

4. 課題点と今後に向けて

 現状では、bluetooth通信が「2秒間送受信→2秒間お休み→2秒間送受信→…(繰り返し)」という謎現象に悩まされています。

 Arduinoのフォーラム2で同じような悩みを抱えている人がいたので、問題解決の参考になりそうです。

 今回はとりあえずテキストでラジコンを操作するのをやめて、ボタンで操作できるようになって良かったです! それではまた!

5. サンプルファイル

 作成したアプリのデータはこちらになります。改良するなど自由に使ってください。

~ MIT App Inventorプロジェクトのファイル(.aia) ~

~ Androidアプリのファイル(.apk) ~

6. 関連記事

7. 参考資料


  1. MIT App Inventor「Bluetooth HC-06. Arduino. Send. Receive. Send text file. Multitouch. Image」、(https://community.appinventor.mit.edu/t/bluetooth-hc-06-arduino-send-receive-send-text-file-multitouch-image/9518↩︎

  2. Arduino FORUM「Bluetooth HC-05 command delay」、(https://forum.arduino.cc/t/bluetooth-hc-05-command-delay/916823↩︎