実験2 サーボモータ


ここで行うこと。


サーボモータは左の写真のようなもので
すでにJ-Botに組み込まれています。


実験2-1 サーボモータを接続する

サーボモータはパルス幅変調の信号で動きを制御することができます。Javelinは任意のI/Oピンを用いてこの信号波形をプログラムで作ることができます。この実験ではピンP12を用います。最初にJavelinは20msの間P12を0V(low)の電圧に設定します。次に1msの間P12を5V(high)の電圧にします。そして、また20msのlowの出力の期間が始まります。そして次は1msと下図のように続いていきます。
パルス列は1msの間highで20msの間lowとなります。これは実験1の音発生に用いた対称のパルス(duty cycle 50%)と異なります。実験1場合、オブジェクトは音発生に典型的に用いられる正弦波をシミュレート(模擬)しています。

この場合、highの時間はサーボモータの動作をコントロールするのに重要な要素で、通常、パルス幅として呼びます。この例では1msのパルス幅で動いています。ある時間lowからhigh(0Vから5V)に行くパルスをポジティブパルスと言います。ネガティブパルスはhighパルスがlowに落ち、静止状態になります。パルス列はdutyとかduty cycleという専門用語で表されます。

重要点
パルス幅はサーボモータをコントロールします。パルス間のlowの時間はサーボモータの機能を損なわないようにするには10msから40msの範囲で使えます。


右のサーボモータはP12、左のサーボモータはP13となっています。
右図のようにP12、P13は専用のソケットがありますからそこに接続してください。左右を間違えたり、挿入する方向を間違えないようにしましょう。コードの色を確認しながら挿入してください。


課題2-1-1

次のプログラムの動作を確認せよ。

高スピードで右のサーボモータを動かす
import stamp.core.*;

/**
 * 高スピードで右のサーボモータを動かす
 */

 public class JBotServo1 {
  static PWM pwmR = new PWM(CPU.pin12); // 右のサーボを作る

  public static void main() {
    pwmR.update ( 110, 2000 ) ;
    pwmR.start () ;

    CPU.delay(10000);   // 1秒間走らせる

    pwmR.stop () ;      // 止める
  }
}


下のプログラムの緑の***の数字を変えて逆回転するようにしなさい。

高スピードで右のサーボモータを動かす
import stamp.core.*;

/**
 * 高スピードで右のサーボモータを動かす
 */

 public class JBotServo2 {
  static PWM pwmR = new PWM(CPU.pin12); // 右のサーボを作る

  public static void main() {
    pwmR.update ( ***,2000 ) ;
    pwmR.start () ;

    CPU.delay(10000);   // 1秒間走らせる


    pwmR.stop () ;      // 止める
  }
}


同様に下のプログラムの緑の***の数字を変えて左のサーボモータが正方向と、逆方向に回転する数値を求めなさい。

高スピードで左のサーボモータを動かす
import stamp.core.*;

/**
 * 高スピードで左のサーボモータを動かす
 */

 public class JBotServo3 {
  static PWM pwmL = new PWM(CPU.pin13); // 左のサーボを作る

  public static void main() {
    pwmL.update ( ***,2000 ) ;
    pwmL.start () ;

    CPU.delay(10000);   // 1秒間走らせる

    pwmL.stop () ;      // 止める
  }
}


上の実験で得た数字を用いて、左右のサーボモータを同時に動かして、前後に動くことを確認しましょう。この時、多分直進はしません。

課題2-1-2

どのような結果になったか報告しなさい。

両方のサーボを高スピードで動かす
import stamp.core.*;

/**
 * 両方のサーボを高スピードで動かす
 */

 public class JBotServo4 {
  static PWM pwmR = new PWM(CPU.pin12); // 右のサーボを作る
  static PWM pwmL = new PWM(CPU.pin13); // 左のサーボを作る

  public static void main() {
    pwmR.update ( ***,2000 ) ;
    pwmL.update ( ***,2000 ) ;
    pwmR.start () ;
    pwmL.start () ;

    CPU.delay(10000);   // 1秒間走らせる

    pwmR.stop () ;      // 止める
    pwmL.stop () ;      // 止める
  }
}


課題2-1-3
  1. 左右のサーボへのupdateの値を逆にするとどのようなことが起こるか確認しなさい。この時、別のプログラムとして作りなさい。つまり、クラス名とファイル名をJBotServo401とかにします。

  2. 両方のサーボが止まる値を求めなさい。

  3. 両方のサーボへの数字を同じ大きな値で動かしなさい。この時の大きな値とは止まる値+70ぐらいを想定します。

  4. 両方のサーボへの数字を同じ小さな値で動かしなさい。この時の小さな値とは止まる値-70ぐらいを想定します。
上の実験でとにかく、動かすことができました。
今度はsofware calibrationで同じようなことを行います。

サーボの中心を補正する
import stamp.core.*;

/**
 * サーボの中心を補正する
 */

 public class JBotServoCalibrate1 {
  static PWM pwmR = new PWM(CPU.pin12); // 右のサーボを作る

  public static void main() {
        // 前の実験で求めた値を入れる。
    int i = *** ; 
    System.out.println ( "Servo calibration" ) ;
    pwmR.update ( i, 2000 ) ;
    pwmR.start() ;            // サーボをスタート

    mainloop: while ( true ) {
      System.out.print ( "Servo value is " ) ;
      System.out.println ( i ) ;
      pwmR.update ( i, 2000 ) ;
      switch ( Terminal.getChar ()) {
      case '+':
        i ++ ;
        break ;

      case '-':
        i -- ;
        break ;

      case 'Q':
      case 'q':
        break mainloop;

      default:
        continue mainloop;
      }
    }

    pwmR.stop () ;
  }
}


iの値は課題2-1-3(2)で求めた値を入れます。+-のキーで値を上下できますからこれで止まる値を確かめてみましょう。この値が以前行った実験と同じであることを確認すること。→違っていたら両方とも再実験する。

課題2-1-4

左のサーボについても同じ事をしなさい。

実験2-2 基本的なサーボモータのクラス

以前の例題はPWMオブジェクトを操作していましたが、これは幾つかの欠点があります。
  1. パルス幅を設定する値を知らないといけないこと。
  2. J-Botを前進させる値が、左右で多分異なること。
  3. 左右で停止点が多分異なること。
そこで、単純なプログラムはこの辺で終わりにして、サーボモータをコントロールするクラスを作ります。ここで行うのは非常に基本的な版で、だんだんと改良していきます。ですから、これはJ-Botをいかに簡単にコントロールできるかを示します。

下のプログラムは、一行目にpackage JBot;があります。これはJBotというパッケージに入れるという意味で、この場合は、今までは、projectというフォルダの中でプログラムを作っていましたが、そのフォルダの下にJBotというフォルダを作り、そこにこのソースプログラムを入れろという意味です。基本的にパッケージは部品ですからmainはありません。従って実行できません。実行はmainのあるクラスから直接あるいは間接的に呼ばれることによって行われます。ですから、ここではコンパイルのみ行ってください。

車輪サーボをコントロールするクラス
package JBot;

import stamp.core.*;

/**
 * 車輪サーボをコントロールするクラス
 * 
 * フリーランニングの車輪サーボに対するPWMサポートを操作する
 * start()で動作を開始し、stop()で停止させる
 */

public class BasicWheelServo extends PWM {
  public int forward;
  public int center;
  public int backward;
  public int low;
  public int pin;


  /**
   * 動作に対するPWMを用いて車輪サーボコントロール・オブジェクト
   * を設定する。単位はPWMの単位である。
   *
   *入力:int pin: PWM信号を発生させるピン(例えば CPU.pin0)
   *     int forward: 前進に対するパルス幅
   *     int center : 停止に対するパルス幅
   *     int backward:後進に対するパルス幅
   *     int low    : パルスに続くlowのパルス幅
   */
  public BasicWheelServo ( int pin, int forward, int center, int backward, int low ) {
    super ( pin ) ;
    this.forward  = forward-center ;
    this.center   = center ;
    this.backward = center-backward ;
    this.low      = low ;
  }

  /**
   * 前進/後進(+/-)に対する車輪スピードを設定する
   *
   * 入力:int percent: 相対スピードのパーセント値
   */
  public void move ( int percent ) {
    if ( percent == 0 ) {
      stop() ;
    } else {
      update(center+((((percent>0)?forward:backward)*percent)/100)
            ,low ) ;
      start () ;
    }
  }
}


問題2-2-1
  1. PWMクラスで使うことのできるメソッドを調べ、その機能を説明しなさい。

  2. オブジェクト変数(インスタンス変数)はどれか。

  3. moveメソッドはどのような値でサーボを動かしているか。

  4. コンストラクタの中にあるsuperは何をしているか。

  5. ? : の意味は理解しているか。これを説明しなさい。
下のプログラムは、上で作ったBasicWheelServoのメインプログラムです。2行めにあるimport JBot.*はJBotのフォルダの中の全てのクラスを使うことを宣言しています。今のところBasicWheelServoだけですが。
forward,center,backwardは以前に求めた数字を代入してください。

車輪サーボをコントロールするクラスのテストプログラム
import stamp.core.*;
import JBot.* ;

/**
 * 車輪サーボコントロールクラスのテストプログラム
 * 
 * JBotで自由回転車輪に対するPWMサポートを扱う
 * start()で動きを開始し、stop()を用いて停止する。
 */

public class BasicWheelServoTest1 {
  public static void main () {
    BasicWheelServo leftWheel =
      new BasicWheelServo (
              CPU.pin13   // pin
            , ***         // forward
            , ***         // center
            , ***         // backward
            , 2000        // low
            ) ;
    BasicWheelServo rightWheel =
      new BasicWheelServo (
              CPU.pin12   // pin
            , ***         // forward
            , ***         // center
            , ***         // backward
            , 2000        // low
            ) ;

    leftWheel.move ( 100 ) ;
    rightWheel.move ( 100 ) ;

    CPU.delay(10000);   // 1秒間走る

    leftWheel.move ( -100 ) ;
    rightWheel.move ( -100 ) ;

    CPU.delay(10000);   // 1秒間走る

    leftWheel.stop () ;
    rightWheel.stop () ;
  }
}


実験2-3 ソフトウェアでの補正 - 直進させる

直進するように補正します。

車輪サーボをコントロールするクラスを補正するプログラム
import stamp.core.*;
import JBot.* ;

/**
 * 車輪サーボをコントロールするクラスを補正するプログラム
 * 
 * JBotで自由回転車輪に対するPWMサポートを扱う
 * start()で動きを開始し、stop()を用いて停止する。
 */

public class BasicWheelServoCalibrate1 {
  public static void main () {
    BasicWheelServo leftWheel =
      new BasicWheelServo (
              CPU.pin13   // pin
            , ***         // forward
            , ***         // center
            , ***         // backward
            , 2000        // low
            ) ;
    BasicWheelServo rightWheel =
      new BasicWheelServo (
              CPU.pin12   // pin
            , ***         // forward
            , ***         // center
            , ***         // backward
            , 2000        // low
            ) ;


    mainloop: while ( true ) {
      System.out.println( "Enter key" ) ;

      switch ( Terminal.getChar ()) {
      case ' ':
        break ;

      case '+':
        leftWheel.forward ++ ;
        break ;

      case '-':
        if (( leftWheel.forward + leftWheel.center ) > 1 ) {
          leftWheel.forward -- ;
        }
        break ;

      case 'Q':
      case 'q':
        break mainloop;

      default:
        continue mainloop;
      }

      System.out.print ( "Left forward value is " ) ;
      System.out.println ( leftWheel.forward + leftWheel.center ) ;

      leftWheel.move ( 100 ) ;

      CPU.delay ( 2000 ) ;

      leftWheel.stop () ;
    }
  }
}


課題2-3-1

上のプログラムは左のサーボモータのみの補正なので、これを改造して右のサーボモータの補正をする、あるいは両方個別に補正できるようにしなさい。
このプログラムのクラス名は「BasicWheelServoCalibrate2」としなさい。

以上の結果を下の表のようにまとめなさい。
これらの値は絶対的に正しい必要はありません。とにかく前進・後進ができるという程度です。最適値にするのは次の実験になります。

前進  
停止  
後進  


これで、直進と後進ができるようになりましたが、この方法では短い距離の移動はできません。これから行う必要のあることは、2つあり、円弧の動作とフィードバックを用いての動作です。

円弧の動作は旋回と回転があります。旋回(pivot)はその点で前後左右に動かずに回転することです。これは後に行います。

フィードバック制御は今まで全く行ってきていません。これはさらに後で行います。ここではフィードバック無しの動きを用います。このため、動いた距離、動作の正確さはこれから行う補正に依存します。また、この補正は近似なので再現性はほとんどないかもしれません。

課題2-3-2

サーボモータを動かす値は単純に比例することは無く、下のグラフのようにある点よりほとんど変化しなくなります。従って比例する部分のみを用いて動作させなければならないことは容易に分かると思います。ですから、上の問題でうまく動作する点を見つけたとしてもスピードコントロールに失敗するかもしれません。これを避けるには比例する限界の点を用いなければなりません。さらに実際には左右のサーボモータが同じ特性とは限らないので、その範囲でスピードコントロールできる値を見つける必要があります。この値が正しく設定されないと、この後の実験がうまくいきませんので、正しい値を見つける必要があります。そして、100%のスピードで前進、後進が出来ることを確認しなさい。

これを行なうにはサーボモータの値を左右それぞれ決めて1秒間に走る距離を測定します。これにはかなりの時間がかかりますので、これを自動化しましょう。
自動化するには計測するためのセンサーが必要になります。これには色々な方法がありますが、CdSセルを用いて車輪の1回転時間を計測することにします。これは実験6で光センサーによるJBotの誘導などに用い、詳しくはそこで行ないますので詳細な説明は実験6を参照してください。

CdS(photoresistor)
CdSセルは、硫化カドミウムを主成分とした光導電素子で、一般にCdSeセルを含めてCdSセルと総称されています。 特徴は、可視光線に対して高感度で小型で軽量、しかも比較的安価に作れます。また、電気伝導度と光量の直線性の範囲が狭く、応答性があまり良くありません。そのため、ゆるやかな照度変化の検出に限定されてしまいます。 CdSセルは光導電面の製作方法から大別して単結晶形、焼結形、蒸着形などありますが、現在は高感度で大面積のものが得られやすい焼結形のものが最も多く使用されています。


以下のソースPhotoresistorをJBotディレクトリに入れてコンパイルのみ行なってください。これはCdSにあたる光の量を計測するクラスです。

Basic Photoresistorクラス
package JBot ;

import stamp.core.*;

/**
 * Basic Photoresistor Class
 * 
 * CPU.rcTimeを用いてphotoresistor回路をテストする
 */

public class Photoresistor {
  public int pin ;
  public int timeout ;
  public int chargeTime ;
  public int bias ;
  public boolean state ;

  /**
   * 8.68μs単位でRCの時間の値を得る
   *
   * 入力:int pin:  用いるCPU.pin
   * 入力:boolean state: 初期のRCの状態
   * 入力:int timeout:  rcTimeの最大の戻り値
   * 入力:int chargeTime: RC回路の充電/放電時間 ミリ秒
   * 入力:int bias:
   */
  public Photoresistor ( int pin, boolean state, int timeout, int chargeTime, int bias ) {
    this.pin = pin ;
    this.timeout = timeout ;
    this.chargeTime = chargeTime ;
    this.state = state ;
    this.bias = bias ;
  }

  /**
   * 8.68μs単位でRCの時間の値を得る
   *
   * @returns RC time
   */
  public int rcTime() {
    // Measure RC time for photoresistor.
    CPU.setOutput ( pin ) ;
    CPU.writePin ( pin, state ) ;     // 充電回路を設定する
    CPU.delay ( chargeTime * 10 ) ;   // 回路を充電する
    int result = CPU.rcTime ( timeout, pin, ! state ) ;
    return ( result > 0 ) ? ( result - bias ) : bias ;
  }
}


回路は下図のように接続します。CdSが車輪の方向に向くように写真のように端に設置します。

コンデンサの表面には3桁の数字が書いてあります。例えば471と書いてあった場合は471pFとか471μFではありません。これは47×101pFです。従って、ここで使うコンデンサは104と印字してあるものを用います。
コンデンサの読み方
制作ビデオ

CdSの光の量を計測するプログラム
import stamp.core.*;
import JBot.* ;

/**
 * CdSの光の量を計測するプログラム
 *
 * CPU.rcTimeを用いてphotoresistor回路をテストする
 */

public class CdS_check {
  public static void main() {
    Photoresistor Photo = new Photoresistor ( CPU.pin3, true, 250, 2, 0 ) ;
    while ( true ) {;
      System.out.print (Photo.rcTime()) ;
      System.out.print("\n");
    }
  }
}


CdSに十分光があたるようにし、車輪につけた紙片が通過したときに値が大きく変わるようにします。その変化の中心あたりの値(閾値、threshould)を下のプログラムのthにセットします。前の実験で求めた停止の値をstopに、その値の前後±25~±30の値をそれぞれbeginとendに設定します。下のプログラムは右の車輪用です。また、停止の値が複数存在するサーボモータもありますから、その時は各自プログラムを修正をしてください。

車輪サーボのスピードを計測する
import stamp.core.*;
import JBot.* ;

/**
 * サーボの数字とスピードとの関係を計測する。
 * 計測は車輪にCdSを一周ごとに覆うようにして行なう
 * 時間計測はtimerHiとtimerLoを8ビット右シフトして用いる
 * 一周の時間は10回計測し平均する
 */

public class JBotServoCalibrate3 {
  static PWM pwm = new PWM(CPU.pin12);    // サーボを作る
  final static int th = ***;              // CdS の閾値
  final static int begin = ***;           // 始点
  final static int end   = ***;           // 終点
  final static int stop  = ***;           // 停止の値
  final static int N     = 10;            // 平均する回数

  public static void main() {
    int i=begin ;
    boolean state=false;
    int wheel;
    int T=0, timeLo=0, timeHi;
    int count, sum;
    int t=0;

    Photoresistor Photo = new Photoresistor ( CPU.pin3, true, 250, 2, 0 ) ;
    pwm.update ( i, 2000 ) ;
    pwm.start() ;             // サーボをスタート
    System.out.println ( "Servo speed check" ) ;
    Timer timer = new Timer(); // タイマースタート
    timer.mark();
      for(i=begin; i<=end; i++) {
      if(i==stop) i++;         // 停止点は飛ばす
      pwm.update ( i, 2000 ) ;
      count=0;
      sum = 0;
      do {
        wheel = Photo.rcTime();    // CdS の値を読む
        if(state) {
          if(wheel < th) {
             state = false;
             timeHi = timer.tickHi();  // タイマーは32bitで
             timeLo = timer.tickLo();  // 16bit*2
             T = (timeHi<<8) + (timeLo>>>8); // そのままでは
             sum += (T-t)/N;          // 精度が良すぎるので
             t = T;                   // 8bit右シフトして使う
             count++;                 // N 回平均する
          }
        }
        else {
          if(wheel > th) state = true;
        }
      } while(count<N);
      System.out.print(i);
      System.out.print(" ");
      if(i < stop) System.out.print("-");
      System.out.println(sum);
    }
    pwm.stop () ;
  }
}


上記の実験より、サーボーモータへの値対スピードのグラフを描きなさい。

エクセルで計測データを二つの列に分離する方法
  1. 文字列を分けたいセルを選択します。
  2. 「データ」をクリックします。
  3. 「区切り位置」をクリックします。
参考:スピードについて

左の車輪の特性も取り、両方のモータが比例して動作する限界点を求めなさい。これを用いて正しく直進・後進することを確かめなさい。

時間がない場合には、左右のモータの特性はほぼ同じと仮定して、まず右のモータの値を決定した後、それに対応する左のモータの値を求めて直進・後進するのを確認しなさい。

課題2-3-3

  1. updateメソッドでlowの値を変化させ、何処までサーボモーターが動くか検証しなさい。

  2. updateメソッドで作れる最小と最大のパルス幅はどのくらいか。

  3. 1分間に4回転という極めて遅い前進を行うためにはどのような値が必要か調べなさい。

  4. 前の問題で、後進の場合はどうか。
問題2-3-3



  1. スピードのグラフより誤差のグラフを求めなさい。

実際の値 - 予想値
誤差[%]=-----------------×100%
予想値
ヒント

実験2終わり