2015年9月23日水曜日

サインスマート InstaBots いろいろ 上手くいかないこと まとめ

なんとなく動くようになったものの、InstaBotsにはまだまだ課題が多い。
ここでちょっとまとめておこう。

課題は私のInstaBots特有のものなのか、現状のプログラムやメカでは設計的にどうしてもそうなるのか、よくわからないけど今後プログラムを解析していったらわかるのではないかと期待している。
頑張れ、未来の私。

ということで、今回は課題のまとめだけ。

1)スピードを上げると倒れる。
単純にスピードを上げただけなのに倒れるのは情けない。フルスピードでも安定してほしい。

2)なかなか止まらない。
少しスピードが出ると、逆方向にジョイスティックを倒してもなかなか止まらない。
バランスを取りながら止まるのは難しいようだが、イメージとしては後に一度倒れそうになってからブレーキをかけるような感じにできればいいのだが。

3)坂道を登れない。
ほんのわずかな坂道も登れない。メカ的なトルクの問題かもしれないが、なさけない。

4)坂道で停止しておられない。
登り坂でリモコンのジョイスティックを離すと下がってしまう。坂道でもその場で停止してほしい。

5)電源が入った状態で持ち上げるとやたらと車輪が回る。
ちょっとびっくりする位回るので、暴走みたいで怖い。当然これはタイヤが回らないことを期待するのだが。


6)一度倒れた後、手で起こすとタイヤがやたらと回って立たない。
手で起こしたら、スッと立ってほしい。


7)その場で回転できない。
方向を変える時、右と左のタイヤを逆に回転させてその場で回転、ということができない。その場でくるくるっと回ってほしいな。









サインスマート InstaBots  ちゃんと立ち止まってよ

InstaBotsを作ったのはいいのだが、リモコンを操作しない状態で、電池の位置によって前に進んだり後に進んだり。ちゃんと止まってくれない。重心を調整すれば止まるのだが。

で、プログラムでなんとかできないの、とスケッチを見ていたらそれらしいものが。

#define Angle_offset 0 

この値を変えてみると、前に進んだり後に進んだり。これだ!(たぶん)
スケッチ全体を理解していないので合っているかどうかわからないが、一応現象面ではOKな気がする。名前がそれらしいし。

一応、いろいろ値を変えてみて以下のような値にすると同じ所でふらふらする状態となった。当然、電池とかの置き方によって変わってくるので、この値は自分専用ということになる。

#define Angle_offset 1.9 


~~~END~~~

サインスマート InstaBotsを改造


 とりあえず動くようになったInstaBotsだが、結構こけたりぶつかったりしてダメージが大き過ぎるので、壊れる前に何らかのプロテクターを考えることにした。

方法はシンプル。アルミの細い板(幅10mm、厚み2mm)をリング状にして、一番下のスタッド固定ナットに共締めした。ネジの長さがギリギリだけど。


Instabots    前から見たところ

Instabots   後ろ姿

これで倒けてもそれほど大きなショックはなくなった。

写真ではUNO基板が3段目ではなく2段目に移動しているが、これは電池が中間のスペースに入り切らなかったので仕方なく移動したもの。
2段目にUNO基板を入れるにはケーブルがちょっと邪魔になるので(頑張れば入るかも?)下の写真のような縦横変換ピンを作り、メスメスケーブルでモーター基板と接続した。

まっすぐなピンを折り曲げた縦横変換ピン

縦横変換ピンで、ケーブルを横からピンに接続している状態


ちなみにセンサーシールドの青い色の電源コネクターに5Vを入れているのだが、これでいいのかどうかは不明。一応動いているんだけど。センサーシールドの資料が見つけられていないので。だれか教えて〜〜〜。

~~~ END ~~~

サインスマート InstaBotsを動かす

組み立てが終わってInstaBotsを動かして見た。

Arduino IDEを使ってプログラムをInstaBotsに書き込むと立たないまでも何となく立とうとしている感じ。
なにやら調整が必要というのは分かっていたので、シリアルモニターを見ながらInstaBotsのポテンショメーターを調整する。

■Kp、Kd、Ki の調整
まず、Kp。ポテンショメーターを回すとKp値が変わる。動きの違いはよくわからない。それほど顕著に変わるものではないみたい。時々暴れる。ポテンショメーターのピンの接触が悪いせいだろうか。とりあえず標準値の17に合わせる。

次にKd。ポテンショメーターを回すと、、、Kdに変化なし。あれ?
プログラムを見てみると、KpはA0端子、KdはA2端子の値を見ているようだ。

  kp = analogRead(A0)*0.1;  Serial.print("  kp=");Serial.print(kp);
  kd = analogRead(A2)*1.0;  Serial.print("  kd=");Serial.print(kd);

で、センサーシールド基板をよく見ると、ポテンショメーターを差し込んだコネクタは左からA0、A1、A2と並んでいる。ポテンショメーターをA0とA1に差し込んでいたのが間違いだったようだ。写真を見て適当に差し込んだのが間違いだった。A0はOKだが、もう1つはA2に差し込む必要があった。となりに移動して、くるくる回すとKd値が変化した。
これまた、標準値840に合わせる。

Ki値は固定値だということで、固定値をプログラムに書き込む。

  ki = 0;  Serial.print("  ki=");Serial.print(ki);
これを次のように変更
  ki = 0.1;  Serial.print("  ki=");Serial.print(ki);


この状態で何となく立っている感じ。ただし、やたら前に行こうとする。
じっと立っていない。
Kp値とKd値をいろいろいじっても安定性が変わるだけで、前に行こうとするのは変わらず。Ki値も同様。
とりあえず、安定のいいところの値はKp=20、Kd=1200、Ki=0.1となった。

ポテンショメーターをこのままにしても動くのだが、接触不良で暴れたりするので、プログラムに固定値を書き込む。ポテンショメーターはもうはずしても大丈夫。

  kp = 20;  Serial.print("  kp=");Serial.print(kp);  //proportion
  kd = 1200;  Serial.print("  kd=");Serial.print(kd); //differentiation
  ki = 0.1;  Serial.print("  ki=");Serial.print(ki);  //immigration


■前に行く問題
電池の位置を前後に変えると前に行こうとしたり、後に行こうとしたりするので重心位置の問題のようだ。そこでおもりをつけてバランスを取ることにした。
(実はプログラムで調整できることが後でわかったのだが。その話はまたあとで。)


■リモコンで操作

なんとなく立っている状態になったので、リモコンを使ってみた。
前にジョイスティックを倒す。。。あれ?左に曲がる。
後にジョイスティックを倒す。。。右に曲がる。
それではと、右にジョイスティックを倒す。。。前進!
で、左だと後進。

なんで? リモコンを90度回転させて持てばちょうどいいのだが、原因を突き止めないと。

リモコンのプログラムを見てみると、

  Joystick_1_X = analogRead(A0);
  Joystick_1_Y = analogRead(A1);

しかし、リモコンのセンサー基板を見ると、XがA1、YがA0に入っている。逆だ。
センサー基板の違いなのかな?よくわからんが、配線を変えるか、プログラムを変えるかする必要がありそう。配線はきれいに平行に入っているので変えたくなく、プログラムを変更することにした。

 Joystick_1_X = analogRead(A1);
 Joystick_1_Y = analogRead(A0);

ついでに左のジョイスティックも同様に変更しておいた。どのみち、コメントアウトされてるんだけど。

この状態で動かすと、まだ右と左が逆だったので、モーターの配線を左右逆にすることにした。なんとなく、他の人の映像を見ると前後が逆になっているような気もするのだが、まあどっちでもいいので、これでリモコン問題は解決。

次は少し改造です。
〜〜〜END〜〜〜

サインスマート InstaBots を購入 組み立て


Arduinoがらみで、「サインスマート UNO R3互換 自動平衡 二輪車キット InstaBots 電子自作 for Arduino」を購入した。完全な衝動買い。

Ver1とVer2があったが、LCDがあるかないかの違いのように思えたので安い方のVer1を購入した。



届いたものは、意外と立派な?プラスチックケースに入っていた。
部品がいっぱいでわくわく。
InstaBots Kit


参考にしたサイトは以下。
www.instructables.com/id/instaBots-Upright-Rover/
・・・Ver1の作成記事
www.instructables.com/id/How-to-build-SainSmart-instaBots-Upright-Rover-Kit/
・・・Ver2の作成記事
www.sainsmart.com/sainsmart-balancing-robot-kit.html
・・・サインスマートのサイト。動画あり。


組み立てはいろいろトラブった。
・配線が短くて届かない。(自分でケーブルをある程度は用意しないといかんみたい)
・短いスタッドのネジをなめてしまった。(もともとネジ部が短過ぎるのよ。)
・リモコンの右ジョイスティックの端子にケーブルを接続すると、スタッドに干渉する。
(なんでこんなになるかなあー、ちょっと信じられない)

センサーシールドが参考サイトの写真などとは違っていて、Ver2.0で使われているものになっていた。
なので、配線がいまいち???状態。Ver2.0のほうの写真なんかを参考にしていった。



組み立てや配線などいろいろ細かくレポートすれば参考になると思うのだが、
あまりやりすぎると「楽しみ」がなくなると思うので、あえて書かないでおく。(ホントは集中してやっていて全く写真が取れていないから)

プログラムはAmazonサイトに記載されていたところからダウンロードした。
https://s3-ap-northeast-1.amazonaws.com/sain-amzn/20/20-011-401/Balancing+Robot+Materials.zip

なんだかんだあったが、とりあえず完成!!、、、写真がない。残念。改造後の物はあるのだけれど。



忘れてました。電池。
ラジコン用の電池が推奨されているようだが、充電器も購入しなきゃいけないので、本体より高くなりそう。
そこで、サンヨーの単三充電池Eneloopを10本(1.2V×10=12V)使うつもりで電池ホルダーを発注していた。けど、よく考えたら10本充電するのが面倒だなと。それに手持ちの電池も数が足りない。

悩んでいたが、ふと電動ドライバーの電池(Makita BL1013)がいいのではないかと、思いついた。この電池は小さいのでなんとか乗っかりそう。リチウムイオンで10.8V。いける。端子はオートバックスで平板の端子を購入し、厚みが薄かったので少し半田を盛って使った。


Makita BL1013




さて次は動かしてみます。

〜〜〜〜END〜〜〜〜

2015年9月16日水曜日

アナログ ライントレースセンサー サンプルスケッチ


【概要】
センサー:QTR-8A フォトリフレクタアレイ
センサーモジュールには8つのフォトリフレクタが付いているが、UNO互換ボードのアナログポートの8つのうちKEY用に1つ使っているため、7つのフォトリフレクタを使用。






 
//****************************************
//7ポート分のアナログ入力値をシリアルポートとLCDに一定時間毎に出力
//シリアルには0から1023までの数字が半角スペースを伴って7つ出力される。
//例)  556 755 100 234 567 890 1023
//LCDには2桁に圧縮された値が表示される
//
//****************************************

//地面とセンサーの距離により値がかなり変わる
//左右端のセンサー値はそれ以外より大きな値になる。
//地面に平行でない場合、左右で値が変わる(角度センサーに使えそう)
//個々のセンサー値に個体差があるようだ
//
//


#include <MsTimer2.h>   // timer interrupt
#include <LiquidCrystal.h>
LiquidCrystal lcd(8,9,4,5,6,7); //lcdタイプの変数を定義

//初期設定*********************************
void setup() {
  
  MsTimer2::set(100, AnalogIn);   //タイマー100ms period
  MsTimer2::start();               //タイマースタート
  //アナログピンは自動的に入力となるので、設定不要。
  Serial.begin(115200);            //Serial Portを設定
  lcd.begin(16, 2);                  // LCD 行と列を設定
}

//メインループ******************************
void loop() {                      
  
}

//タイマー割り込み処理***********************
void AnalogIn() {
   int i;
   long val[8];
   String strVal[8];
   
   //1からまで7のアナログポートを読む
   for (i=1;i<=7;i++) {          
    val[i] = analogRead(i);           
   }
   
   //AD値をシリアルポートに出力
   for(i=1;i<=7;i++){
    Serial.print(" "+String(val[i]));
   }
   Serial.println();   //7つのポート出力をしたら、改行コードを出力

   //AD値をLCDに表示

   //数値を文字列2桁にする
   for(i=1;i<=7;i++){
    val[i]= val[i]*100/1024;   //0-99の値にする
    strVal[i]=String(val[i]);  //文字にする
    if (strVal[i].length()==1) {  //一桁なら前にスペースをつける
      strVal[i]=" " + strVal[i];
    }
   }
   //1行目 (AD1,AD3,AD5,AD7)  
   lcd.home();            
   for(i=1;i<=7;i+=2){
    lcd.print(strVal[i] + "  ");
   }
   //2行目 (AD2,AD4,AD6)
   lcd.setCursor(2,1);
   for(i=2;i<=6;i+=2){
    lcd.print(strVal[i] + "  ");
   }


}

2015年9月14日月曜日

超音波センサー N個同時 サンプルスケッチ



//********************************************************************
//超音波センサをN個同時に使って距離をserial通信するプログラム
//計算を早くするため、cm以下の精度は出さないとした。
//音波が戻ってこない場合、0cm と出力する。
//Trigerパルスを同時に出す方式。ハードはN個のTrigerを接続すること
//計測のタイムアウトは40ms。タイマー割り込みで測定している。
//********************************************************************
#include <LiquidCrystal.h>
#include <MsTimer2.h>  // Timer interrupt

LiquidCrystal lcd(8,9,4,5,6,7); //lcdタイプの変数を定義
const long serialBps = 115200; //シリアル通信レート
//超音波センサー
const int sensor = 2; //センサーの数
const int pinTrig = 2; //トリガーパルス出力用ピン番号 
const int pinEcho[sensor] = {3,12}; //エコーパルス入力用ピン番号 [x]要素番号がセンサー番号
long Duration[sensor] ; //測定したエコーパルス幅 usec [x]要素番号がセンサー番号
long Distance[sensor] ; //測定した距離 mm [x]要素番号がセンサー番号
unsigned long StartTime; //パルス長を測り始める時間
int EchoState[sensor] ; //Echoポートの状態 0:LOW 初期状態 1:HIGH 2:LOW
volatile int CountDownTimer =0;  //1msのタイマー割り込みでカウントダウンされ、ゼロで止まるタイマー


//SETUP*****************************
void setup() {

  // LCDの行と列を設定
  lcd.begin(16, 2);

  //シリアルポート使用開始
  Serial.begin(serialBps);
  
  //pin modeの設定
  pinMode( pinTrig, OUTPUT);
  pinMode( pinEcho[0], INPUT);
  pinMode( pinEcho[1], INPUT);

  //タイマー割り込みの設定
  MsTimer2::set(1,count);    // 1ms period
  MsTimer2::start();            //タイマースタート

  //LCDへタイトルを表示
  lcd.home();               // カーソル位置を1行目に設定
  lcd.print("Ultrasonic x N");
  lcd.setCursor(0,1);
  lcd.print("Output to Serial");
  
}

//MAIN LOOP*************************
void loop() {

  //=================================
  //超音波センサー output = Distance[i]
  //=================================

  //超音波を出力(トリガーパルスの出力)
  digitalWrite( pinTrig, HIGH );     //HIGHを出す
  delayMicroseconds( 10 );           //10usec
  digitalWrite( pinTrig, LOW );      //LOWに戻す
  
  //Echoパルス幅を測る準備  
  for (int i=0; i<sensor; i++){
    EchoState[i]= 0;     //ポートのステータスを初期化
  }
  CountDownTimer = 40;   //40msのカウントダウンタイマーをセット(タイムアウト用)

    
  //Echoパルス幅の時間を測定 usec単位
  while(true) {
      
       //各センサーのEchoの値(HIGH/LOW)をチェックし,状態を記憶する
       //HIGH-->LOWとなったら、パルス幅時間を求める
       for (int i=0; i<sensor; i++){
         int dR =digitalRead(pinEcho[i]);
         
         //HIGH?
         if (( dR == HIGH) && (EchoState[i] == 0)) {     // HIGH=1 , LOW=0
           EchoState[i] = 1;             //Echoポートの状態:0-->1に変更
           StartTime = micros();         //時間測定開始時間を記録
         }
         
         //LOW? HIGH-->LOW?
         if ((dR == LOW ) && (EchoState[i] == 1)) {
           EchoState[i] = 2;             //Echoポートの状態:1-->2に変更
       
           //パルス幅の時間を開始時間と現在時間から求める
           unsigned long ms =micros();
           if (ms > StartTime) {
             Duration[i]= ms-StartTime;
           }
           else {
             Duration[i] = 4294967295-StartTime+ms;
           }
         }
       }
       
       //全てのセンサーの計測が終了したら、whileループを抜ける。      
       int EchoStateSum = 0;
       for (int i=0; i<sensor; i++){
         EchoStateSum += EchoState[i];
       }
       if (EchoStateSum == 2*sensor) {   //全てのセンサーの計測が終了していたら、echoStateSumの値は2×センサー数となる。
        break;
       }

       //タイムアウトになったら、whileループを抜ける。
       //タイムアウトになったら、計測が終わっていないセンサーの計測値は全てzeroを入れる。
       if ( CountDownTimer == 0){
        for (int i=0; i<sensor; i++){
         if (EchoState[i] < 2){
          Duration[i] = 0;
         }
        }
        break;
       }
  }


  //距離(単位mm)を計算。
  for (int i=0; i<sensor; i++){ 
    Distance[i] = Duration[i]*340/2/10000;  // 音速を340m/sとする.往復距離を半分にする
  }
  

  //=================================
  //Serial 通信
  //=================================
  
   //シリアルに距離を出力 
  Serial.print("Distance:");
  
  for(int i;i<sensor;i++){
    Serial.print(Distance[i]);
    Serial.print("cm ");
  }
  Serial.println();    
}

//TIMER INTERRUPT**************************
void count() {
  //タイマーをカウントダウンする。ゼロで止める。
  if ( CountDownTimer != 0){
    CountDownTimer--;
  }
}