実験6 光センサーを使う


ここで行うこと。

実験6-1 製作と光センサー



実験2で用いましたが、もう一度CdSを用いますので再確認のためPhotoresistorクラスを再掲載します。

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 ;
  }
}


Photoresistorクラスが仕事の大部分を担っています。各々のオブジェクトは1つのピンを扱います。コンストラクタはピン番号などの詳細を格納します。仕事はrcTimeメソッドによってなされます。通常、RC回路は一度パラメータが決めれば問題ありません。rcTimeメソッドは始めにコンデンサ回路を充電しCPU.rcTimeメソッドを用いてその回路がいかに早く回復するかを求めます。bias値は始めの例、PhotoresistorSensor1では0にします。次の例、PhotoresistorSensor2では値を変えます。その違いは、素子は均一ではないからです。例えば220Ωの抵抗は、実際にはその値ではなく、部品によっては5%~20%の誤差を持っています。同様にコンデンサとか、CDSセル(Photoresistor)でも同じように誤差を持っています。このbias値はこれらを計算に入れるためにあります。このようなことは今までにサーボモータのところでも行いました。

右の図のような回路を下の図のようにブレッドボード上に作ります。
下図ではP6に配線してありますが、回路図どおりにP5につなげてください。
コンデンサの表面には3桁の数字が書いてあります。例えば471と書いてあった場合は471pFとか471μFではありません。これは47×101pFです。従って、ここで使うコンデンサは104と印字してあるものを用います。★コンデンサの読み方



Photoresistor1クラスは各々の入力ピンに対してPhotoresistorクラスを単純に用いています。

Photoresistor1
import stamp.core.*;
import JBot.* ;

/**
 * 基本的なPhotoresistorのテストプログラム
 * 
 * CPU.rcTimeを用いてphotoresistor回路をテストする
 */

public class Photoresistor1 {
  public static void main() {
    Photoresistor leftPhoto  = new Photoresistor ( CPU.pin5, true, 250, 2, 0 ) ;
    Photoresistor rightPhoto = new Photoresistor ( CPU.pin3, true, 250, 2, 0 ) ;


    while ( true ) {
      // 左右のphotoresistorのRC時間を測定する
      // RC時間の測定値を表示する

      System.out.print   ( "L  " ) ;
      System.out.print   (leftPhoto.rcTime()) ;
      System.out.print   ( "  R  " ) ;
      System.out.println (rightPhoto.rcTime()) ;
    }
  }
}


Photoresistor1のプログラムは2つのPhotoresistorオブジェクトを作ります。それからrcTimeメソッドを用いて測定値を表示します。 課題6-1-1

できるだけ左右のセンサーの明るさを同じにしてbiasの値を決めなさい。そのために、左右のrcTimeの値の差を表示するようにしなさい。

Photoresistor2のプログラムはPhotoresistor1のプログラムを少し手直ししたものです。

基本的なPhotoresistorのテストプログラム
import stamp.core.*;
import JBot.* ;

/**
 * 基本的なPhotoresistorのテストプログラム
 * 
 * CPU.rcTimeを用いてphotoresistor回路をテストする
 */

public class Photoresistor2 {
  public static void main() {
    Photoresistor leftPhoto  = new Photoresistor ( CPU.pin5, true, 250, 2, 0 ) ;
    Photoresistor rightPhoto = new Photoresistor ( CPU.pin3, true, 250, 2, 30 ) ;


    while ( true ) {
      // 左右のphotoresistorのRC時間を測定する
      // RC時間の測定値を表示する

      System.out.print   ( "L  " ) ;
      System.out.print   ((leftPhoto.rcTime()+5)/10) ;
      System.out.print   ( "  R  " ) ;
      System.out.println ((rightPhoto.rcTime()+5)/10) ;
    }
  }
}


一つ目の違いはbiasの値30を右のセンサーに与えていることです。これは各自前の実験で得た値を入れてください。もう1つは表示値を10で割って四捨五入していることです。これは少しの光の変化では値が変らないようにしています。

課題6-1-2

  1. 明るい光が当たった時の閾値を求めなさい。
  2. コンデンサの値を一桁大きい場合と小さい場合でどのように変るか試しなさい。

実験6-2 Sensorクラス

Photoresistorクラスの問題点はCPU.delayを用いていることです。小さなdelay値を用いていますが、マルチタスクシステムでは他のタスクに影響を与えてしまいます。rcTimeメソッドを使うことは同様に他のタスクに影響を与えますが、そのdelay時間は充電時間よりも小さくなっています。これは仮想周辺装置としてバックグラウンドで動作させるには良いですが、Javelinのレパートリーにはありません。

PhotoresistorSensorはオーバーヘッドを減らすためにマルチタスクの構成要素、PhotoresistorSensorTaskが必要です。この二つは各々のphotoresistorセンサーをポーリングするタスクと一緒に動作します。photoresistorは物体とか光源までの距離を求めるのではなく光強度であるので、センサーオブジェクトは触覚センサーと僅かに異なっています。

光強度は物体を模擬するのに用います。暗い部分は物体に接近していて、明るい部分は開いた領域とみなします。次の例は物体から逃れて、光の方向にJ-Botを移動させるものです。 PhotoresistorSensor

Photoresistor Sensor クラス
package JBot ;

import stamp.core.*;
import java.lang.Math.* ;

/**
 * Photoresistor Sensor クラス
 * 
 * PhotoresistorTaskを用いてphotoresistorセンサーを用いてテストする
 * 光強度が閾値以下ならば障害物は無い
 */

public class PhotoresistorSensor extends BaseSensor {
  protected PhotoresistorSensorTask sensorTask ;

  protected int direction ;
  protected int distance ;
  protected boolean obstacleDetected = false ;
  protected int lowerLimit ;
  protected int deadband ;

  /**
   * PhotoresistorSensorオブジェクトを作り、タスクをサポートする
   *
   * 入力:int leftPin: 用いるCPU.pin
   * 入力:int rightPin: 用いる CPU.pin
   * 入力:boolean state: 初期のRCの状態
   * 入力:int timeout: 最大のrcTimeの戻り値
   * 入力:int chargeTime: RC回路の充電/放電時間 ミリ秒
   * 入力:int bias: bias offset, left side >0から right <0を引いたもの
   * 入力:int lowerLimit: 障害物がないときの最小距離
   * 入力:int deadband:
   */
  public PhotoresistorSensor ( int leftPin
                             , int rightPin
                             , boolean state
                             , int timeout
                             , int chargeTime
                             , int bias
                             , int lowerLimit
                             , int deadband ) {
    sensorTask = new PhotoresistorSensorTask
                       ( this
                       , leftPin
                       , rightPin
                       , state
                       , timeout
                       , chargeTime
                       , bias ) ;
    this.lowerLimit = lowerLimit ;
    this.deadband = deadband ;
  }


  /**
   * 障害物が検出されたかどうかを表す
   * 通常、ポーリング対イベントの時に用いる
   *
   * 戻り:障害物が検出された時true
   */
  public boolean obstacleDetected () {
    sensorTask.checkSensors() ;

    return obstacleDetected ;
  }

  /**
   * 障害物の初期位置を示す
   * 左右にある物体の検出は前方とするシンプルな検出システム
   *
   * 戻り: 障害物の相対方向 (left, right, etc.)
   */
  public int obstacleDirection () {
    return direction ;
  }

  /**
   * 指定された方向の障害物の距離を得る
   * noneという値は何も検出されなかったことを示す
   *
   * 入力: 距離を得る方向
   *
   * 戻り: 指定された方向の障害物の距離
   */
  public int obstacleDistance ( int direction ) {
    return distance ;
  }

  /**
   * センサー情報に基づいて結果を更新する
   * 結果が利用できる時にセンサータスクによって呼ばれる
   *
   * 入力:int resultLeft: 左側のphotoresistorのrcTimeの結果
   * 入力:int resultRight: 右側のphotoresistorのrcTimeの結果
   */
  protected void saveResults ( int resultLeft, int resultRight ) {
    // 現在の結果は有効
    // 障害物のステータスを保存する
    switch (   (( resultLeft > lowerLimit ) ? 1 : 0 )
             + (( resultRight > lowerLimit ) ? 2 : 0 )) {
    default:
    case 0:
      obstacleDetected = false ;
      break;

    case 1:
      direction = left ;
      distance = resultLeft ;
      obstacleDetected = true ;
      break;

    case 2:
      direction = right ;
      distance = resultRight ;
      obstacleDetected = true ;
      break;

    case 3:
      // 両方のセンサーは障害物を示している
      if ( Math.abs ( resultLeft - resultRight ) < deadband ) {
        // 両方の距離は接近している
        direction = front ;
        distance = ( resultLeft > resultRight ) ? resultLeft : resultRight ;
      } else if ( resultLeft > resultRight ) {
        // 左のセンサーは高い値
        direction = left ;
        distance = resultLeft ;
      } else {
        // 右のセンサーは高い値
        direction = right ;
        distance = resultRight ;
      }
      obstacleDetected = true ;
      break;
    }
  }
}


このクラスは最後の障害物についての詳細を保持するオブジェクトを作ります。コンストラクタに引数を通してPhotoresistorSensorTaskを作り、PhotoresistorSensorオブジェクトを作るこのオブジェクトは1つのオブジェクトだけ必要になります。そのタスクは隠されたままです。

BaseSensorアブストラクト・メソッドが定義され、最新の障害物情報を単純に戻します。obstacleDetectedメソッドはそのタスクのcheckSensorsメソッドを同様に呼びます。

saveResultsメソッドは最新の値を設定するタスクによって用いられます。その結果はbiasですでに補正され、四捨五入され、そのメソッドは範囲外と光強度閾値に取り組む必要があります。

PhotoresistorSensorTaskクラスの定義は次のようになります。

PhotoresistorSensorTaskクラス
package JBot ;

import stamp.core.*;
import stamp.util.os.* ;

/**
 * PhotoresistorSensorTaskクラス
 * 
 * PhotoresistorSensorをサポートする
 * 他のオブジェクトから直接呼んではいけない
 */

public class PhotoresistorSensorTask extends Task {
  protected PhotoresistorSensor sensor ;

  protected int leftPin ;
  protected int rightPin ;
  protected int timeout ;
  protected int chargeTime ;
  protected boolean pinState ;
  protected int resultLeft ;
  protected int bias ;

  final static int startChecking = 1 ;
  final static int startLeftPin = 2 ;
  final static int startRightPin = 3 ;

  protected PhotoresistorSensorTask
              ( PhotoresistorSensor sensor
              , int leftPin
              , int rightPin
              , boolean pinState
              , int timeout
              , int chargeTime
              , int bias ) {
    this.sensor = sensor ;
    this.leftPin = leftPin ;
    this.rightPin = rightPin ;
    this.timeout = timeout ;
    this.chargeTime = chargeTime ;
    this.pinState = pinState ;
    this.bias = bias ;
  }


  /**
   * 実行中でないかどうかセンサーをチェックする
   */
  protected void checkSensors() {
    if ( state == stopped ) {
      // タスクは終了しているので、再度開始する
      nextState (startChecking) ;
      start () ;
    }
  }

  /**
   * photoresistorセンサーをチェックする
   * バイアスのために四捨五入して補正する.
   * 範囲外の条件に対処する
   *
   * 入力:int pin: チェックするピン
   * 入力:int bias:もし正の場合差を取るバイアス値
   */
  protected int rcTime ( int pin, int bias ) {
    int result = CPU.rcTime ( timeout, pin, ! pinState ) ;

    // 範囲外の条件に対処する。 つまりtimeoutを超えた時間
    if ( result == -1 ) {
      result = timeout*2 ;   // 大きい数を用いる
    } else if ( bias > 0 ) {
      // もし値が正だったらバイアス補正する
      result -= bias ;
    }

    return (result+5)/10 ;
  }

  /**
   *マルチタスクのサポート
   */
  public void execute () {
    switch ( state ) {
    case startChecking:
      // Setup to charge circuit
      CPU.setOutput ( leftPin ) ;
      CPU.setOutput ( rightPin ) ;
      CPU.writePin ( leftPin, pinState ) ;
      CPU.writePin ( rightPin, pinState ) ;

      // 充電回路に対してdelayを設定する
      sleep(chargeTime,startLeftPin);
      break;

    case startLeftPin:
      // photoresistorのRCtimeを測定する
      resultLeft = rcTime ( leftPin, -bias ) ;
      nextState(startRightPin);
      break;

    case startRightPin:
      // 結果を報告する
      sensor.saveResults ( resultLeft, rcTime ( rightPin, bias )) ;
      // stop()にそのまま行く
    default:
    case initialState:
      stop() ;
      break;
    }
  }
}


コンストラクタは対応するPhotoresistorSensorオブジェクトを含めて引数を格納します。センサーのオブジェクトはタスクへの参照も管理するので二つは互いに作用します。

タスクは3つのメソッド、checkSensors, exxecute, rcTimeからできています。checkSensorsメソッドはobstacleDetectedメソッドを用いてセンサーオブジェクトがポーリングされた時に、対応するPhotoresistorSensorオブジェクトによって周期的に呼ばれます。checkSensorsメソッドはstartChecking状態のなかでタスクをリスタートします。

executeタスクは走っているタスクにあります。initialStateはタスクを停止させます。checkSensorsメソッドによってstartChecking状態の中でリスタートされます。この状態はPhotoresistor回路でコンデンサを充電するピンを設定します。充電完了まで時間がかかりますが、Javelinを停止する代わりに、マルチタスクシステムは他のタスクを走らせます。充電は必要な時間以上に長いかも知れませんが、コンデンサが過充電になることはありません。

startLeftPin状態はsleepのタイムアウトが起こったときに実行されます。rcTimeメソッドは左のセンサーピンをチェックするために呼ばれます。タスクはコントロールを渡し、他のタスクが走る機会を与えた後、startRight状態を実行します。この時点で右のセンサーピンはモニターされ、両方のセンサーからの結果saveResultsメソッドを呼ぶことによってセンサーオブジェクトに与えられます。タスクは停止します。breakが無いのでそのまま下に行きstop()を実行することに注意してください。

タスクオブジェクトはセンサーオブジェクトによって周期的に開始されますが、そのタスクは一連の結果を求めるには十分な長さです。これは、Photoresistorセンサーをチェックするオーバーヘッドなしにセンサーの変化に応じて大きな動作をさせることが出来ますが、この動作がセンサーオブジェクトを用いていない時での話です。

2つのオブジェクト、マルチタスク法を用いて重要な複雑性があります。PhotoresistorSensorはマルチタスク環境で素晴らしく動作しているが、オーバーヘッドはCPU.delayを用いているPhotoresistorクラスでは普通にあります。同様に様々なマルチタスクで用いられるBaseSensorインターフェースでも動作します。

PhotoresistorSensorのコンストラクタの2つの引数はシステムの感度をコントロールします。lowerLimitdeadbandがあります。lowerLimitは障害物無しと障害物を検出する間の境界に設定します。deadbandは左右間の障害物指示あるいは前方の障害物指示の感度をコントロールします。大きい値は障害物検出が左右より前方を指示しやすくなります。

これらの2つのクラスをテストするためにPhotoresistorSensor1を次に示します。

基本的なPhotoresistorのテストプログラム
import stamp.core.*;
import stamp.util.os.* ;
import JBot.* ;

/**
 * 基本的なPhotoresistorのテストプログラム
 * 
 * CPU.rcTimeを用いてphotoresistor回路をテストする
 */

public class PhotoresistorSensor1 extends Task {
  PhotoresistorSensor sensor =
      new PhotoresistorSensor ( CPU.pin5    // left pin
                              , CPU.pin3    // right pin
                              , true        // pin state
                              , 250         // rcTime limit
                              , 2           // timeout
                              , 30          // bias
                              , 3           // lower limit
                              , 5 ) ;       // deadband

  public void execute() {
    // Only uses one state
    if ( sensor.obstacleDetected ()) {
      System.out.print   ( "Dir=" ) ;
      System.out.print   (sensor.obstacleDirection()) ;
      System.out.print   ( "  Dist=" ) ;
      System.out.println (sensor.obstacleDistance(sensor.obstacleDirection())) ;
    } else {
      System.out.println ( ".") ;
    }
  }

  public static void main() {
    new PhotoresistorSensor1 () ;

    Task.TaskManager () ;
  }
}


以前のマルチタスクのプログラムと同様に、このクラスの定義はTaskクラスとTask.TaskManagerを走らせるメインメソッドを組み込んでいます。

課題6-2

  1. 明暗を作ることの出来る場所で、2つのセンサーの上で手で影を作ると、どのような情報が表示されるか調べなさい。
  2. 各々のセンサーに入射する光量を調節して方向と大体の距離を記録しなさい。センサーに入射する光量が低くなると距離の値は大きくなるでしょう。
  3. 左右のセンサーを正しく設定しなさい。

実験6-3 光羅針盤(コンパス)

J-Botの前に懐中電灯の光を集光した時に、今まで行ってきた回路とプログラミング技術を使えばその光の方向にJ-Botを向かせることができます。photoresistorが光の方向の区別が出来るように正しく向いているかどうかチェックしてください。J-Botの中心から45゜外側を向いていて、水平より45゜下を向いているようにしてください。

光に向くJ-Botをプログラムする

J-Botが光源を追跡するようにするには、各々のphotoresistorで測定された値を比較するプログラムを作ることです。光を暗くするとphotoresistorの値が大きくなることを思い出してください。もし、右のphotoresistorの値が左のphotoresistorの値よりも大きい場合、左の方が明るいことを意味しています。この比較はすでにPhotoresistorSensorオブジェクトでなされています。もし左よりも右の方が明るかった場合45゜方向に障害物があると指示します。同様に障害物方向が135゜の場合には左側が明るいことになります。障害物が直前、すなわち135゜と報告した場合はオブジェクトはdeadbandを持ちます。この場合、両方のセンサーは光を検出していなければいけませんが、同じ必要はありません。

基本的なPhotoresistor Compassテストプログラム
import stamp.core.*;
import stamp.util.os.* ;
import JBot.* ;

/**
 * 基本的なPhotoresistor Compassテストプログラム
 * 
 * CPU.rcTimeを用いてphotoresistor回路をテストする
 */

public class PhotoCompass1 extends Task {
  protected JBotInterface jbot = new BasicJBot ( new MultitaskingJBot()) ;
  protected PhotoresistorSensor sensor =
      new PhotoresistorSensor ( CPU.pin5    // left pin
                              , CPU.pin3    // right pin
                              , true        // pin state
                              , 250         // rcTime limit
                              , 2           // timeout
                              , 30          // bias
                              , 3           // lower limit
                              , 5 ) ;       // deadband

  public void execute() {
    // 1つの状態のみ用いる
    if ( sensor.obstacleDetected ()) {
      if ( sensor.obstacleDirection() <= 45 ) {
        // 障害物が左にある。右に回転する。
        jbot.pivot(jbot.continuousRight) ;
      } else if ( sensor.obstacleDirection() >= 135 ) {
        // 障害物が右にある。左に回転する。
        jbot.pivot(jbot.continuousLeft) ;
      } else {
        // 直前にある
        jbot.stop() ;
      }
    } else {
      // 障害物はない
      jbot.stop() ;
    }
  }
}


PhotoCompass1はマルチタスクを用いていますが、以前に用いたAvoidObstacleTaskではありません。これはAvoidObstacleTaskは固定の動作であるからで、PhotoCompass1タスクは全体的な回転の一部ではなく細かい動きを用いています。PhotoCompass1タスクはまだPhotoresistorSensorオブジェクトを用いていますが、モーター制御にBasicJBotオブジェクトを加えています。

executeメソッドはPhotoresistorSensorBasicJBotオブジェクトの間のインターフェースを担っています。executeメソッドはinitialStateがうまく動作するように永久に1つの状態のままで維持するから状態変数を用いていません。

executeメソッドはobstacleDetectedがtrueを返すかどうかをチェックします。そしてその障害物が前方に、左に、右に、あるかどうかをチェックします。この場合、障害物は暗いものとします。もし障害物が前方に無かった場合、J-Botは逆方向に旋回します。光が無くなった時および光が直前の場合に止まります。

pivotLeftpivotRightメソッドはそれぞれの方向にJ-Botを回転開始させます。光が無くなったり光が直前に検出されるまで停止しません。ですから通常J-Botは無限に旋回はしません。どちらのメソッドを呼んでもパルスは生じません。その代わり、基本的なPWMオブジェクトは最新設定に基づくパルスを発生します。その値をリセットすることは、次のパルスが作られる時にその値が用いられるようになります。このすべてはBasicJBotクラスによって隠蔽されています。

課題6-3

  1. 異なった明るさの場所で実験してlowerLimitの効果を調べなさい。
  2. PhotoCompass1の中で呼ばれるpivotLeftpivotRightメソッドを逆にしてJ-Botを動作させなさい。どのような動作になるか。

実験6-4 光を追う

J-Botに単純に前進動作を加えることによって光を探すロボットに変身させることが出来ます。試みるための興味ある実験はJ-Botを前進させ光を探すようにプログラムすることです。そして、外が明るく開いた1つのドアのある暗室の中に入れます。この途中には何の障害物も無いことを仮定すると、J-Botはドアに向かって行き、暗室を出るでしょう。

光追跡をプログラミングする

J-Botが光追跡をするようにプログラミングするには、PhotoCompass1クラスをほんの僅か修正するだけです。jbotオブジェクトが停止するところに二箇所の変更があります。どのように動くか実験してみましょう。

基本的なPhotoresistor Compassテストプログラム
import stamp.core.*;
import stamp.util.os.* ;
import JBot.* ;

/**
 * 基本的なPhotoresistor Compassテストプログラム
 * 
 * CPU.rcTimeを用いてphotoresistor回路をテストする
 */

public class PhotoCompass2 extends Task {
  protected JBotInterface jbot = new BasicJBot ( new MultitaskingJBot ()) ;
  protected PhotoresistorSensor sensor =
      new PhotoresistorSensor ( CPU.pin5    // left pin
                              , CPU.pin3    // right pin
                              , true        // pin state
                              , 250         // rcTime limit
                              , 2           // timeout
                              , 30          // bias
                              , 3           // lower limit
                              , 5 ) ;       // deadband

  public void execute() {
    // 1つの状態のみ用いる
    if ( sensor.obstacleDetected ()) {
      if ( sensor.obstacleDirection() <= 45 ) {
        // 障害物が左にある。右に回転する。
        jbot.pivot(jbot.continuousRight) ;
      } else if ( sensor.obstacleDirection() >= 135 ) {
        // 障害物が右にある。左に回転する。
        jbot.pivot(jbot.continuousLeft) ;
      } else if ( sensor.obstacleDistance ( 90 ) > 20 ) {
        // 暗すぎる。停止して待つ
        jbot.stop() ;
      } else {
        // 直前にある
        jbot.move(jbot.continuousBackward) ;
      }
    } else {
      // 障害物はない
      jbot.move(jbot.continuousForward) ;
    }
  }

  public static void main() {
    new PhotoCompass2 () ;

    Task.TaskManager () ;
  }
}


最初のは簡単です。始めのobstacleDetected呼び出しに対するelse節のjbot.stop()メソッドがjbot.move(jbot.continuousForward) に変っています。これは明るい光が前方にあった時に前進するようにします。

2番目の変更はちょっとしたコントロールをします。この場合、つまりobstacleDirectionが90(45以下ではなく、135以上でもない)の時にsensor.obstacleDistanceメソッドが呼ばれます。距離をチェックするif文はJ-Botが停止するか前進するかです。その距離の閾値は非常に暗い場所に行った場合にJ-Botを停止するようにします。もしJ-Botが止まるべきではない時は、この条件を取り除いて前進させることもできます。距離閾値はJ-Botが停止する時をコントロールします。

課題6-4

photoresistorを前方ではなく上方に向けた場合どのような動きになるか実験してみよう。

実験6-5 ライントレース

J-Botが前方の懐中電灯の光を追うようにプログラムできたならば、黒い背景にある白線を追跡できないだろうか? J-Botは黒い背景上の白線を追うことはできます。これがここの実験です。同じ意味で、白い背景で黒線を追うこともできます。線の色にかかわらず一般的にライントレースという言葉を用います。

黒線の幅は大体5cmぐらいが適当です。光の条件によって補正が必要になります。影とか明るい光はエラーの元ですから、できるだけ一様な光環境にしましょう。例えば、窓からの光は無しで、蛍光灯の光のみにします。

ライントレースのプログラミング

以前に作ったプログラムの様々なパラメータを変えることによってJ-Botは白の背景上の黒線を追うことができます。LineFollower1はこれをデモします。PhotoresistorSensorのコンストラクタのlowerLimitdeadbandは小さい値になっています。差が大きい時にはdeadbandはうまく動作するように大きくします。場合によっては、PhotoresistorSensorクラスの中で感度減少をしているところを増加することになります。するとlowerLimitdeadbandはそれなりに調節する必要があります。非常に明るい部屋の場合はdeadbandを減らすだけでは不十分で、コンデンサを一桁小さいものに代える必要があります。



基本的なPhotoresistor Compassテストプログラム
import stamp.core.*;
import stamp.util.os.* ;
import JBot.* ;

/**
 * 基本的なPhotoresistor Compassテストプログラム
 * 
 * CPU.rcTimeを用いてphotoresistor回路をテストする
 */

public class LineFollower1 extends Task {
  protected JBotInterface jbot = new RampingJBot ( new MultitaskingJBot ()) ;
  protected PhotoresistorSensor sensor =
      new PhotoresistorSensor ( CPU.pin5    // left pin
                              , CPU.pin3    // right pin
                              , true        // pin state
                              , 250         // rcTime limit
                              , 2           // timeout
                              , 30          // bias
                              , 1           // lower limit
                              , 2 ) ;       // deadband

  public void execute() {
    // 1つの状態のみ用いる
    if ( sensor.obstacleDetected ()) {
      if ( sensor.obstacleDirection() <= 45 ) {
        // 障害物が左にある。右に回転する。
        jbot.pivot(jbot.continuousRight) ;
      } else if ( sensor.obstacleDirection() >= 135 ) {
        // 障害物が右にある。左に回転する。
        jbot.pivot(jbot.continuousLeft) ;
      } else if ( sensor.obstacleDistance ( 90 ) > 20 ) {
        // 暗すぎる。停止して待つ
        jbot.stop() ;
      } else {
        // 直前にある
        jbot.move(jbot.continuousForward) ;
      }
    } else {
      // 障害物はない
      jbot.move(jbot.continuousForward) ;
    }
  }

  public static void main() {
    new LineFollower1 () ;

    Task.TaskManager () ;
  }
}


もし光を消したらJ-Botは停止します。これは前に障害物が検出されたときの範囲テストを行ったから覚えているでしょう。これは道筋の終わりに到達した時にJ-Botを停止させる時に用いることができます。当然光の条件により範囲テストの値を調節する必要があります。

参考

課題6-5

  1. ラインを45゜方向転換するようにして試しなさい。
  2. 90゜にした時も行いなさい。
問題6

  1. 光量に応じて抵抗値が変わるCdS素子について述べなさい。

  2. RC回路で抵抗値を推測するためにJavelinは何を測定しているか?

  3. CPU.rcTimeの測定で増加は何を意味するか?

  4. CdSの値が増加するときは何を示唆しているか?

  5. 光を追従するJ-Botの対するプログラムは暗がりを追うJ-Botとどのように異なるか?

  6. 直進する傾向のあるJ-Botにおいてdeadbandの役割は何か? 方向を変える傾向にあるJ-Botにおいてそれはどんな役割をしているか?

  7. 10μFのコンデンサを用いたときにCPU.rctimeが150としたとき、CdSの抵抗値はいくらか?

  8. 上の問題で0.1μFのときはどうだろうか? もし、0.1μFのコンデンサを0.01μFのコンデンサに取り替えた場合どのような問題が起こるだろうか? 測定時間におけるRCの値が増加した場合にはどんな影響が起こるだろうか? サーボモータのパフォーマンスに測定時間がどのように影響するか?

  9. J-Botが前進した後で少なくとも1秒間の固定長の音を鳴らすようにPhotoCompass2に音を付け加えなさい。500ミリ秒の音を発生するためにTaskToneGeneratorクラスを使いなさい。

    PhotoCompass2のタスクのタイマーは、タイマーとして使えるようにスリープメソッドが呼ばれないので用いることが出来ます。ヒント:J-Botが前進したときにtimer.mark()を呼びなさい。timer.timeoutSec(1)は少なくとも1秒経過したことを示します。

    タイマーが走り、音が発生したことを保持するためにbooleanの変数を用いなさい。

    このプログラムは決して連続音を発生しません。何故か? ヒント:音とタイムアウトの持続時間を考えなさい。

  10. 上の問題で、前進音が鳴らされたときを保持するbooleanの変数の代わりに異なったstateを用いて実装しなさい。

  11. 触覚をJ-Botに付けなさい。途中に障害物があるライントレースを開発しなさい。ラインを追従し、障害物を監視するための触覚をチェックするようにJ-Botをプログラムしなさい。障害物の周りを回ってラインに戻るルーチンを開発しなさい。

  12. ライントレースに対するdeadbandに関係する興味ある一面はソフトウェアで純粋に補正できることです。ここでは、deadbandの設定とラインの幅との関係を調べます。

    上の課題を幅3.75cmの黒いテープを用いて再度行いなさい。自分の作ったJ-BotのCdSの幅を変えてはいけません。変えるのはdeadbandの設定だけです。この実験を2.5cmの幅のテープでも再度行いなさい。各々のテープの幅に対するdeadbandの上限と下限を記録しなさい。言い換えると、ライントレースが成功するdeadbandの設定の最大と最小値を見つけなさい。その結果をグラフに表しなさい。deadbandとテープの幅にはどんな数学的な関係があるか? 線形関係の近似を行うグラフを用い、deadbandの等式を求めなさい。この結果を4.4cm幅のテープで等式をテストしなさい。

実験6終わり