ブログだいちゃん

ブログだいちゃん

趣味のブログ

ブログだいちゃん

Balancing Robot Car その2 rotary encoder を使用する_2

f:id:blogdaichan:20190103161338j:plain

作成準備が整いましたので配線を行っていきます、YouTube参考になる動画を見付けましたのでこの方法で作成します。

回路図およびライブラリー、スケッチ

上記動画のリンクからダウンロード出来ます、圧縮ファイルを解凍すると回路図・ライブラリー・スケッチの各フォルダが出来ますのでそれらを使用しました。

回路図の内容に一部不明(間違?)な部分もありますが適宜修正して配線を行いました。

問題点は大まかに以下のようなものです。

MPU6050の5VがVIN(7.4V)接続されている⇒5V端子に変更 エンコーダーのVCCは3.3Vなのに5Vに接続されている⇒3.3V端子に変更 TB6612FNGの端子配置は回路図と違う現物を見ながら配線する、等々。

配線および組立

上記の問題を踏まえてユニバーサル基盤にパーツを配置し、配線を行い組立ました。

f:id:blogdaichan:20190103161340j:plainf:id:blogdaichan:20190103161333j:plainf:id:blogdaichan:20190103161329j:plain

Bluetoothについては配線はしましたが、今のところ使用していません、PIDなどの調整が出来た時点で設定をします。

試験走行

スケッチはディフォルトのままボードに書き込み、起動させてみました。

f:id:blogdaichan:20190103161347j:plainf:id:blogdaichan:20190103161344j:plain

あっけなく立ちました、ちょっと敏感な感じですがPIDの調整でなんとかなりそうです。

動作試験

この後の作業は

Bluetooth module を使用してスマフォでコントロール出来るようにしたいのですが、初めて行うことなので上手くいくかどうか。(^_^;)

Balancing Robot Car その2 rotary encoder を使用する_1

f:id:blogdaichan:20190103162937j:plain

前の記事で初めて倒立ロボカーを作成しました、ある程度の時間バランスを保っていますが、安定性がなく常にフラフラ動いており満足するものではありませんでした、PIDの設定を調整しながら試しましたが思うようには動かず、一応倒立したので良しとしますが、モヤモヤ感が拭いきれません。

YouTube などの動画では、ロータリーエンコーダー付のモーターを使用したものが安定して動作しています、ロータリーエンコーダーを使えば安定するのかと、軽く考え作ってみることにしました。

使用パーツ類

Arduino MEGA 2560互換機

ロータリーエンコーダー付モーター(DC6V仕様)

3軸ジャイロスコープMPU6050

モータードライバー:TB6612 FNG

Bluetooth_モジュール:HM-10

ユニバーサル基板:7cm×9cm

アクリル板:180mm×320mm 好きな色

バッテリー:リチウムイオン18650 2本充電器付

バッテリーケース:18650用直列2本3Dプリンターで作成

長ネジ:4mm×150mm 4本ナット・ワッシャ含む、ホームセンターなどにあります。

スペーサー:3Dプリンターで作成

3mmネジ・プラスチックスペーサー・配線用ワイヤー・ピンヘッダー・コネクタなど。

作成準備

3mmのアクリル板からMotor取り付け部・電池及びマイコン部・天板の3枚をCNCで切削、大きさは125mm×70mm。

f:id:blogdaichan:20190103162933j:plainf:id:blogdaichan:20190103162929j:plainf:id:blogdaichan:20190103162925j:plain

スペーサーとバッテリーケースを3Dプリンターで作成。

長ネジ用のスペーサーは50mmと87mmを各4本PLAで作成。

f:id:blogdaichan:20190103162921j:plain

電池フォルダーはTPAフィラメントで作成。

f:id:blogdaichan:20190103162917j:plainf:id:blogdaichan:20190103163001j:plain

電池ケースはPLAで作成しアクリル板に挟み込みます。

f:id:blogdaichan:20190103162957j:plainf:id:blogdaichan:20190103162950j:plainf:id:blogdaichan:20190103162944j:plain

モータードライバー・ジャイロ・Bluetoothモジュールなどを載せるシールドをユニバーサル基盤で作製します、Arduino MEGAに合わせて切断しピンヘッダーを取付けます。

f:id:blogdaichan:20190103162940j:plain

作成準備が整いましたので、次は配線をして試験走行を行います。

黒ラブ・ダイスケ7月で5歳になります

f:id:blogdaichan:20190103164436j:plain

愛犬、黒ラブのダイスケ我が家の一員になってから早5年が経ちます、朝晩の散歩は欠かさないものの、広い草原での運動は暫くぶりです。

f:id:blogdaichan:20190103164441j:plainf:id:blogdaichan:20190103164433j:plain

獣医の先生にダイエットしなさいと言われ、去年の秋からダイエット用ドックフードを与え続けて体重が36Kgから33Kgまで3Kgほど下げることができました。

いつも春先から食欲不振になっていたのが、減量のお陰か今年は食欲旺盛です。

Balancing Robot Car の作成

Voice Kit がらみでマイコンに手を染めてから面白さにはまりました。

比較的分かりやすい Arduino を使用してソナーを使ったRobot Car などを作成して楽しんでおります。

今回は Balancing Robot Car に挑戦してみます、難解な PID の設定などありますので上手くいくかどうか心配ですがとりあえず挑戦します。

使用パーツ

Arduino NANO

NANO用 I/Oシールド

タミヤ ダブルギヤボックス

11.1V850mAリポバッテリー

3軸ジャイロスコープ:MPU6050

3端子レギュレーター NJM7809_9V1AArduino 電源用 (秋月電子通商

NJM2396F63_6.3V1.5A:モーター駆動用 (秋月電子通商

モータードライバー:L298N (秋月電子通商

トグルスイッチ

ユニバーサル基板

配線用ワイヤーヒートシンク フレーム(3Dプリンタで作成)など

Balancing Robot Car の組み立て

フレームは3Dプリンターで作成、モータードライバー及びレギュレーターは当初フレームに直付けしましたが、端子間がショートしそうなのでユニバーサル基板に配置しフレームに取付けました。

3セル11.1Vのリポバッテリーを使用しましたので、モーターの電源供給用に6VのレギュレーターとArduino電源用の9Vレギュレータを使用しています。

またレギュレーター及びモータードライバーが発熱しますので、手持ちのヒートシンクを加工して取付けました。

3軸ジャイロスコープのMPU6050はジンバルに使用していた物を流用しました。

動作確認

当初3軸ジャイロスコープの取付け方向の勘違いで全然立たなく、試しにジャイロスコープを外し手に持って試したところ、取付け位置が右に90度ズレていることが判明、左に90度戻し取付けてなんとか立つようになりました。

しかし、電源を入れる前に上手くバランスを取った上で電源を入れないと立ってくれません、まだまだ調整が必要ですね。

フラフラしながらもバランスを取っているところの動画です、なぜか右に回り始めます、左右のモーターのミスマッチなのか他に原因があるのか今のところ分かりません。

スマートフォン無しでVoice Kitを日本語設定する方法

f:id:blogdaichan:20190103165551j:plain

Voice Kit の関連記事を投稿しましたが、コメントを頂いた中にスマートフォン以外で日本語設定出来ないかとのコメントが何件かありました。

通常、Vioce Kit イメージをRaspberry Pi にインストールし 手順に従って設定後、スマートフォンGoogleアプリなどで言語を日本語にしないと日本語で話してくれません、この方法以外で設定したことがなかったのでPC等での設定方法は分かりませんとお答えしていました。

Android-X86を使用してPCをAndroid化する

Android-X86は、X86プラットフォームにAndroidをインストール出来るようにするものです、これを使用してPCでVoice Kitの日本語設定を行うことが出来そうなのでやってみました。

古いPCなどありましたらインストールしてAndroide端末として使用すれば、もたついていたPCも快適に動くかもしれません、私は古い WindPad-110WにインストールしAndroidパッドとして使用しています。

またUSBメモリから立ち上げればPCにインストールしなくてもAndroidが立ち上がりますので、Voice Kitの日本語設定などはこの方法が良いでしょう。

必要なもの

USBメモリ(4GB程度あれば十分です)

Android-X86イメージファイル、Android-X86プロジェクトに行きダウンロードファイル一覧をクリック、cm-x86-14.1-r2.iso(520MB)をダウンロードします。

次に窓の杜でブータブルUSBメモリツール「rufus」をダウンロードします。

使用したPCはヤフオクで落としたPanasonic CF-NX2(Windows10化したもの)

インストール方法

空のUSBメモリをPCに差し込み「rufus」を立ち上げます、次にダウンロードしたcm-x86-14.1-r2.isoを指定してUSBメモリに書き込みます、「rufus」の使用方法は以下を参照して下さい。

参考:https://www.gigafree.net/system/os/Rufus.html

f:id:blogdaichan:20190103165548j:plainf:id:blogdaichan:20190103165545j:plain

Android-X86を立ち上げる

まず最初に、PCのBIOS設定でUSBメモリが最初に立ち上がるように設定しておきます、BIOSの種類によって方法が異なりますのでPCのマニュアルなどを確認して下さい。

USBメモリをPCに差し込みPCを立ち上げますと以下のような画面になりますのでUSBから立ち上げるには一番上を選択します、PCにインストールするには上から3番目を選択します。

f:id:blogdaichan:20190103165542j:plain

暫く待つと言語の設定の画面に切り替わります、途中エラーメッセージが出ますが無視して下さい、言語⇒日本語に、新規としてセットアップ選択、ネット接続の設定、アップデート確認、メールアドレス及びパスワード設定⇒メールアドレスはVoive Kit でプロジェクト名などを設定したときに使用したものを指定します(@はShift+2)、ホームアプリ⇒Trebuchet、以上設定が終わるとAndroid-X86の画面が立ち上がります。

f:id:blogdaichan:20190103165518j:plain f:id:blogdaichan:20190103165529j:plain f:id:blogdaichan:20190103165524j:plain f:id:blogdaichan:20190103165521j:plain f:id:blogdaichan:20190103165538j:plain

言語を日本語に設定する

さてAndroid が立ち上がりましたので Voice Kit の言語を日本語に設定します、使用するアプリは「Googleアプリ」です、横に並んでいるアイコンの右側にある「G」のマークのアイコンです、でもこのアプリのヴァージョンが古いのでこのまま立ち上げても設定出来ません、アップデートで自動的にいくつかのダウンロードが始まりますが、もしアップデートされない場合は「G」の右側の「PLAYストア」を立ち上げ「Googleアプリ」を検索し呼び出して強制的にアップデートします。

f:id:blogdaichan:20190103165535j:plain

アップデート出来たら「Googleアプリ」を立ち上げ右下の3本線の設定をクリック⇒左端の歯車マークの設定をクリック⇒Googleアシスタントの設定をクリックして、出た画面のディバイス一覧の中に「Voice Kit」等、登録したデディバイス名が表示されているはずです、その名前をクリックし、ディバイスのアドレス:自分の住所、アシスタントの言語:日本語、に設定して終了です。

Android-X86の画面には終了ボタンがありません、PCの電源ボタンを押すと電源を切る・再起動するなどのボタンが表示されます。

赤外線リモコンでロボカーを動かす 番外編

f:id:blogdaichan:20190103175150j:plain

前の記事で赤外線リモコンと、ソナーを使ったロボカーを作成しましたが、ロボカーのスピードが速すぎてコントロールが難しいので、モーターの回転数をさげたいと思い調べたところ。

モーターの回転数を制御するには

L298Nにはモーターの回転数を制御出来るENABLE A と ENABLE B 端子があります、ここはショートピンでHIGHTに設定されています。

f:id:blogdaichan:20190103175146p:plain

このショートピンを外しArduinoのPWM端子に接続すれば速度制御が出来るとの事、早速試してみることにしました。

Arduinoの現在の使用ピンは以下のようです。

モーターA:6番7番ピン B:8番9番ピン ソナー:10番11番ピン サーボ:5番ピン リモコン:12番ピン

 

設定を変更したがうまく動かない

当初のピン配置を以下のように変更しましたが

モーターA:4番6番ピン ENABLE A:3番ピン モーターB:7番8番ピン ENABLE B:9番ピン リモコン、ソナー、サーボは同様

しかしこれではモーターの速度制御は出来ませんでした、制御には0~255の数値を設定するとそれに応じた速度制御が出来るはずですが、マックスの255ではフルスピードで回転しますがそれ未満ではモーターが回転しないのです、ここではまりました。

TimerとPWM使用にはいろいろ問題が

調べたところPWM 出力と、タイマーや tone() 関数など、組み合わせに制限がある機能のお話に解決策らしい記事がありました、PWM端子ならどこに繋げても良いわけではなさそうです。

で、差し障りのなさそうな 5番と6番ピンを使用することにして、以下のピン配置にしました。 サーボ:3番ピン モーターA:4番7番ピン ENABLE A:5番ピン モーターB:8番9番ピン ENABLE B:6番ピン リモコン、ソナーは前と同様

スケッチの書き込みと動作確認

この設定でスケッチを書き込み動作確認したところ、速度変更が出来ました(^_^)v PWM使用時は注意が必要ですね。

モーターの速度を変更すると、方向転換の時間など影響がでますのでその辺の設定変更も必要になります。

赤外線リモコンでロボカーを動かす 完結編

リモコンカーのパーツも揃ったので作成に掛ります、今回のロボカーはモーター2個とボールキャスター1個の仕様で作成します。

ロボカーのシャーシ作成

シャーシーは前回と同様3Dプリンターで作成しますが、今回は初めてPETGフィラメントを使用しました。

ABSより縁のソリは出ませんが糸引きが結構出ます、堅さはABSよりはありますね。

主要パーツ

Arduino NANO拡張ボードを使用しています、その他モータードライバーリモコンソナーサーボモーターなどです、ボールキャスターはホームセンターにあったものを使用します。

組み立てとスケッチの書き込み

モータードライバーはシャーシーの下に取付けArduino NANOは拡張ボードに差し込み上部に両面テープで固定。

ソナーはサーボホーンと一緒にサーボにネジ止め、赤外線センサーは角度を付けて両面テープでシャーシーに貼り付けました。

モータードライバーへは7.4Vのリポ電池を使用、このモータードライバーは5V出力端子がありますのでここからArduinoに電源供給しています。

スケッチはここを参考にリモコンに合わせて設定しました、リモコンでの操作とソナー使用の自動走行をリモコンで切り替えが出来ます。

スケッチのリンクが切れてますのでスケッチを載せておきます。

#include <IRremote.h>  //赤外線リモコンライブラリー
#include <Servo.h>    //サーボライブラリー

//モーターAの設定
const int enA = 5;     //モーターA速度設定ピン
const int MotorA1 = 4; //固定変数設定(const)
const int MotorA2 = 7; 

//モーターBの設定 
const int enB = 6;     //モーターB速度設定ピン
const int MotorB1 = 8;
const int MotorB2 = 9;

//ソナーピン設定
const int trigger=10;
const int echo=11;

int leftscanval, centerscanval, rightscanval, ldiagonalscanval, rdiagonalscanval; //int(整数型)
char choice; //キャラクター(char)
  
//赤外線コントロール設定
int receiver = 12;         // 信号ピンを12IRrecv irrecv(receiver);  
decode_results results; 
char contcommand;
int modecontrol=0;
int power=0;

const int distancelimit = 27;     //前方障害物の距離制限 27cm          
const int sidedistancelimit = 12; //両側の障害物までの最小距離(12cm)
int distance;                     //距離は整数
int numcycles = 0;
char turndirection;               //どの方向が障害物フリーであるかに応じて 'l''r'または 'f'を取得します。
const int turntime = 600;         //ロボットが回転している時間(ミリ秒)実際に動かし設定する
int thereis;
Servo head;

void setup(){
  head.attach(3);           //サーボは3ピン
  head.write(80);           //サーボ角度80度
  irrecv.enableIRIn();      // 赤外線受信開始
  pinMode(enA, OUTPUT);     //モーターアウトプット設定
  pinMode(MotorA1, OUTPUT);
  pinMode(MotorA2, OUTPUT); 
  pinMode(enB, OUTPUT);
  pinMode(MotorB1, OUTPUT); 
  pinMode(MotorB2, OUTPUT);
  pinMode(trigger,OUTPUT);  //超音波設定
  pinMode(echo,INPUT);

  //変数の初期化(モーターストップ)
  digitalWrite(MotorA1,LOW);
  digitalWrite(MotorA2,LOW);
  digitalWrite(MotorB1,LOW);
  digitalWrite(MotorB2,LOW);
  analogWrite(enA, 0);
  analogWrite(enB, 0); 
  digitalWrite(trigger,LOW);
}
 //前進
void go(){ 
   analogWrite(enA, 102);
   analogWrite(enB, 100); 
   digitalWrite (MotorA1, HIGH);                              
   digitalWrite (MotorA2, LOW); 
   digitalWrite (MotorB1, HIGH); 
   digitalWrite (MotorB2, LOW);
}
 //バック
void backwards(){
   analogWrite(enA, 100);
   analogWrite(enB, 100);  
   digitalWrite (MotorA1, LOW);                              
   digitalWrite (MotorA2, HIGH); 
   digitalWrite (MotorB1, LOW); 
   digitalWrite (MotorB2, HIGH);
}
//sonarで距離測定
int watch(){  
  long howfar;
  digitalWrite(trigger,LOW);
  delayMicroseconds(5);                                                                              
  digitalWrite(trigger,HIGH);
  delayMicroseconds(15);
  digitalWrite(trigger,LOW);
  howfar=pulseIn(echo,HIGH);
  howfar=howfar*0.01657; //障害物がどれくらい離れているかcm
  return round(howfar);
}
//左折
void turnleft(int t){  
   analogWrite(enA, 100);
   analogWrite(enB, 100); 
  digitalWrite (MotorA1, LOW);                              
  digitalWrite (MotorA2, HIGH); 
  digitalWrite (MotorB1, HIGH); 
  digitalWrite (MotorB2, LOW);
  delay(t);
}
//右折
void turnright(int t){  
   analogWrite(enA, 100);
   analogWrite(enB, 100); 
  digitalWrite (MotorA1, HIGH);                              
  digitalWrite (MotorA2, LOW); 
  digitalWrite (MotorB1, LOW); 
  digitalWrite (MotorB2, HIGH);
  delay(t);
}  
 //ストップ
void stopmove(){ 
  digitalWrite (MotorA1 ,LOW);                              
  digitalWrite (MotorA2, LOW); 
  digitalWrite (MotorB1, LOW); 
  digitalWrite (MotorB2, LOW);
}  

void watchsurrounding(){ //左右、右対角、左対角線の距離を測り、それらをcm単位で変数rightscanval、
                         //leftscanval、centerscanval、ldiagonalscanvalとrdiagonalscanvalに代入する
  centerscanval = watch();
  if(centerscanval<distancelimit){stopmove();}
  head.write(120);
  delay(100);
  ldiagonalscanval = watch();
  if(ldiagonalscanval<distancelimit){stopmove();}
  head.write(160); //サーボの可動範囲(角度)を設定する
  delay(300);
  leftscanval = watch();
  if(leftscanval<sidedistancelimit){stopmove();}
  head.write(120);
  delay(100);
  ldiagonalscanval = watch();
  if(ldiagonalscanval<distancelimit){stopmove();}
  head.write(80); //160度スパンの中心角(サーボを180度動かすと90度を使用します)
  delay(100);
  centerscanval = watch();
  if(centerscanval<distancelimit){stopmove();}
  head.write(40);
  delay(100);
  rdiagonalscanval = watch();
  if(rdiagonalscanval<distancelimit){stopmove();}
  head.write(0);
  delay(100);
  rightscanval = watch();
  if(rightscanval<sidedistancelimit){stopmove();}

  head.write(80); //前方を向いて終了
  delay(300);
}

char decide(){   //距離がある方向のキャラクターを設定(l,r,f)
  watchsurrounding();
  if (leftscanval>rightscanval && leftscanval>centerscanval){
    choice = 'l';
  }
  else if (rightscanval>leftscanval && rightscanval>centerscanval){
    choice = 'r';
  }
  else{
    choice = 'f';
  }
  return choice;
}

void translateIR() {      //リモコンキーによる動作
  switch(results.value)
  {
  case 0xFF18E7:          //前進(↑)
    go();
    break;
  case 0xFF10EF:          //左折(←)
    turnleft(turntime); 
    stopmove();  
    break;
  case 0xFF38C7:            //ストップ(OK)
    stopmove();   
    break;
  case 0xFF5AA5:          //右折(→)
    turnright(turntime);
    stopmove(); 
    break;
  case 0xFF4AB5:          //バック(↓)
    backwards();
    break;
  case 0xFF6897:          //自動走行(*)
    modecontrol=0; stopmove(); // 「*」が受信された場合、自動ロボット動作モードに切り替える
    break;
  default: 
    ;
  }// 終了
  delay(500);             // .5秒待機
} 

  //======メインルーチン======
void loop(){ 
  if (irrecv.decode(&results)){    //リモコンが信号を送信しているかどうかを確認する
    if(results.value==0xFFB04F){   //#」が受信された場合は、ロボットをオンにします
      power=1; }
    if(results.value==0xFF9867){   //0」が受信された場合、ロボットをオフにする
      stopmove(); 
      power=0; }
    if(results.value==0xFF6897){   //(*)を受信した場合は、自動ロボット<==>遠隔操作の切替
      modecontrol=1;               //  リモコン操作モードを有効にする
      stopmove();                  //ロボットは停止し、ユーザーの指示に応答します
    }
    irrecv.resume();               // 赤外線を受信する
  }
  
  while(modecontrol==1){  //modecontrol = 0 (*)になるまで、システムはリモート制御モード中にこのループに入ります
    if (irrecv.decode(&results)){   //何かが受信されている場合
      translateIR();                //受信した信号に応じた動作をする
      irrecv.resume();              // 赤外線を受信する
     }
  }
  if(power==1){
  go();                             // 何もなければ前進
  ++numcycles;
  if(numcycles>130){                //前進中に130度以内の障害物を探す 
    watchsurrounding();
    if(leftscanval<sidedistancelimit || ldiagonalscanval<distancelimit){
      turnright(turntime);
    }
    if(rightscanval<sidedistancelimit || rdiagonalscanval<distancelimit){
      turnleft(turntime);
    }
    numcycles=0;                //サイクル数を0にする
  }
  distance = watch();           // watch()関数を使用して前方確認
  if (distance<distancelimit){  // 前方に障害物があれば停止する、超音波センサーの誤動作を無視するため(25回試す)
      ++thereis;}
  if (distance>distancelimit){
      thereis=0;}               //カウント再開
  if (thereis > 25){
    stopmove();                 // 前方に何かあればストップ
    turndirection = decide();   //曲がる方向を決める
    switch (turndirection){
      case 'l':
        turnleft(turntime);
        break;
      case 'r':
        turnright(turntime);
        break;
      case 'f':
        ;                       //前方に何もなければ前進
        break;
    }
    thereis=0;
  }
 }
}

 

配線図はありませんが、スケッチの内容を見ればArduino への配線は分かります。