実験7 赤外線を用いた物体検出


ここで行うこと。



赤外線
赤外線は赤色光よりも波長が長く、ミリ波長の電波よりも波長の短い電磁波全般を指し、波長ではおよそ 1mm ~ 700nm に分布する。すなわち、可視光線と電波の間に属する電磁波と言える。
ここで用いるIR LEDと赤外線検出器は980nmで動作し、これは近赤外線の領域である。
波長(nm)
400
470
565
590
630
780
近赤外800-1000
赤外1000-2000
遠赤外2000-10000


障害物を検出するのに高級なマシンビジョンは必要ありません。非常にシンプルなもので十分です。ある種のロボットはRADAR(radio detection and ranging)やSONAR(sound navigation and ranging;音響測深)を搭載している。SONARは、たまに水中ではなく空中で用いられる時にSODARと呼ばれることがあります。シンプルなシステムでさえ、ロボットの通り道を赤外線で照明したり物体から反射してきた光を判断することができます。テレビなどのリモコンの発達でこれらが手に入りやすくなっています。

J-Botの赤外線物体検出法は色々な使い方があります。J-Botは物体にぶつかる事無しに赤外線を用いて物体を検出することができます。photoresistorを用いて赤外線はライントレースの白黒の違いを検出できます。赤外線はJ-Botから物体までの距離を調べるのに用いることができます。J-Botは決まった距離での物体追跡、高い塊の検出と回避をするためにこの情報を用いることができます。

赤外線のヘッドライト

J-Botに備え付ける赤外線物体検出システムは幾つかの点で自動車のヘッドライトに似ています。車のヘッドライトからの光が物体から反射された時に、人の目は障害物を検出し脳がそれを処理し、筋肉を使って体が適切に自動車を操作します。J-Botではヘッドライトとして赤外線のLEDを用います。J-Botの目は赤外線検出器です。赤外線検出器は物体から反射された赤外線があるかどうかをJavelinに信号として送ります。J-Botの脳はJavelinで、判断をしてこの入力に基づいてサーボモータを操作します。

IR検出器(赤外線検出器)は光学フィルタが入っており、内部のフォトダイオード・センサーで検出したい980nmの狭い光を通します。IR検出器は同様に電気的フィルターも入っており、38.5kHzあたりの信号のみを通します。これは太陽光とか室内照明のような通常の赤外線からの影響を防いでします。太陽はDC(0Hz)で、蛍光灯は100Hz(東京では)です。したがって38.5kHzの信号にはまったく影響はありません。

周波数のトリック

IR検出器は38.5kHz近辺のIR信号のみを見るから、IRのLEDはその周波数でon/offされないといけません。J-Botが発生する実際の周波数は38.4kHzになります。この目的に555タイマーが用いられますが、555タイマー回路は複雑ですがこの実験で用いるには低機能です。例えば、ここで導入しているIR検出法は距離検出に用いることができますが、それには距離検出をするための付加的な回路が555タイマーに必要になります。

二人のJ-Bot熱狂家が興味ある555タイマー法が要らなくなるトリックを見つけました。この方法は信号を正弦波に平滑するRCフィルターなしでPWMオブジェクトを用います。PWMの最高周波数は57.6kHzを送信できるように設計されていますが、フィルタされていないPWM出力は38.5kHzのIR検出器に対して十分使える38.4kHzを発生させることができます。

IR検出はIRのLEDに周波数を変えることによってできます。IR検出器は障害物までの距離に応じて異なった周波数に応答します。このアプローチはJavelinが精度良くパルスを出力できないのでうまくいきません。しかしながら、J-Botではこれを行う良い方法があります。

始めに、JavelinはDAC(digital to analog)仮想周辺装置を持っています。これはステップ的に0~5Vの間の電圧を発生することができます。IRのLEDをこのDACの出力につなぐと、LEDの出力強度はその電圧によって変ります。DAC/IR LEDの組み合わせで38.4kHzのPWM出力と合わせるとJ-Botは強度を変えられる変調出力を発生させることができます。IR検出器を付加すれば、J-BotはIR LED/検出器からの距離に応じて物体を検出できます。J-Botに非常に近い物体はLEDからの光の量にかかわらず検出されます。遠い物体は光が強い時にだけ検出されます。

DAC回路は1kΩの抵抗と10μFのコンデンサを用います。抵抗はDAC仮想周辺出力ピンに接続します。抵抗のもう一方はグラウンドにつながっているコンデンサに接続します。DAC仮想周辺装置はパルスを用いて希望の電圧にするためコンデンサを充電します。IR LEDはこの抵抗とコンデンサに接続されます。LEDのもう一方はPWM出力ピンに繋がっている220Ωの抵抗に接続します。




実験7-1 IR送信/検出器を作りテストする

IR LEDをテストする

課題7-1-1

実験1で行ったようにIR LEDを点滅させてみましょう。それを携帯電話に付いているカメラなどで覗いて下のように人間に見えない赤外線を可視化できることを確認し、証拠写真を撮っておきましょう。
LED点灯
LED消灯


IR対をテストする

各々のIR対を作るキーポイントは38.4kHzでDAC出力を変調することです。1つのIR LEDとその対の検出器は他の回路からの光の干渉を受けないようにしないといけません。

課題7-1-2

次のIrRangeTest1を実行しなさい。

IR距離測定テスト
import stamp.core.*;

/**
 * IR距離測定テスト
 */

 public class IrRangeTest1 {
  static PWM pwmLeft ;
  static DAC dacLeft ;
  static PWM pwmRight ;
  static DAC dacRight ;

  public static int getRange ( DAC dac, PWM pwm, int detectorPin ) {
    int range = 0 ;

    // 仮想周辺装置を開始
    dac.start();
    pwm.start();

    // 光強度をステップアップする
    for (int i = 0; i < 15; i++){
      // 強度レベルを設定する
      dac.update(255 - (13*i));

      // 検出器を安定させる
      CPU.delay(20);

      // 範囲のレベルをカウントする
      if(CPU.readPin(detectorPin)) {
        range++;
      }
    }

    // 仮想周辺装置を停止する
    dac.stop();
    pwm.stop();

    return range ;
  }

  public static void main() {
    pwmLeft  = new PWM(CPU.pin1,1,2);
    pwmLeft.stop();
    dacLeft  = new DAC(CPU.pin7);
    dacLeft.stop() ;
    pwmRight = new PWM(CPU.pin3,1,2);
    pwmRight.stop();
    dacRight = new DAC(CPU.pin8);
    dacRight.stop();

    while(true){
      System.out.print   ( "L " ) ;
      System.out.print   ( getRange ( dacLeft, pwmLeft, CPU.pin0 ));
      System.out.print   ( "   R " ) ;
      System.out.println ( getRange ( dacRight, pwmRight, CPU.pin2 ));
      }
  }

}


準備

  1. プログラムが走っている間、IR検出器は赤外線を反射しない方向に向かせてください。一番良い方法は検出器を天井に向けることです。メッセージウィンドウには左右とも15が表示されるべきです。
  2. IR対の前に手を置くと、メッセージウィンドウの数字は15から0になるはずです。手を取り除くと15に戻ります。これを両方の各々の検出器に対して行います。そして両方の検出器の前に手を置くと両方とも15から0になるべきです。
  3. IR対がこれらの全てのテストにパスした時に動かす準備が整ったことになります。そうでなかったら、プログラムあるいは回路をチェックしてください。


IR距離測定プログラムはどのように動作するか

メインメソッドは仮想周辺装置を割り当てますが、次の仮想周辺装置を割り当てる前に各々は停止させます。これは仮想周辺装置に対するコンストラクタが仮想周辺装置を開始させてしまうからです。

仮想周辺装置の数
Javelinでは同時に使える(active)仮想周辺装置の数は6個です。仮想周辺装置を止めることはinactiveにすることで、開始させることはactiveにすることです。


メインメソッドのwhileループは永久に回ります。これはデバッガーを用いるプログラムでは一番の方法です。これは各々のDAC/PWM対に対する距離の結果を表示します。距離を決定する仕事はgetRangeクラスメソッドによってなされます。今の時点ではオブジェクトを作る必要がないのでクラスメソッドを用いています。

getRangeメソッドはメソッドの最初で両方の仮想周辺装置を開始させ、メソッドが終わる時に両方を停止させます。これは2つの仮想周辺装置だけが同時にactiveであることを意味しています。forループは16回繰り返し、DACを用いて電圧を生じます。dac.updateの値は255で5Vに対応します。これはその電圧は5Vから開始しステップダウンしていくことを意味しています。最小値は60(255-(13*15))、すなわち1Vです。IR LEDは値がこの点より下の時に光を発生します。

LEDが変調信号の出力を開始した後、僅かなdelayがあります。これはPWMの周波数更新がその周波数に即時にPWMオブジェクトに変更を加えないためにあります。同様に、反射してきた赤外線に応答するIR検出器に対するdelayでもあります。

ループの中で各々の反復でIR検出器の応答をチェックします。理論的には、障害物を検出していない状態から検出の状態への遷移は矛盾が無く起こりますが、実際にはIR検出器からの信号には変動があります。IR検出器の結果を数えることによって1,2の変動はあるでしょう。

課題7-1-3

  1. 例題で用いている0~15の代わりに異なった距離範囲を用いて実験しなさい。dac.update呼び出しの乗数を変更しなければいけないのに気をつけてください。細かくするとより多くの情報が引き出せるか? 多くの変動は正確さにどのようにかかわってきているか?
  2. 距離のテストの時に異なった色の物体で試してみなさい。異なった色や、異なった模様の場合、同じ距離の結果になるだろうか?


実験7-2 検出クラス - 赤外線

赤外線センサーのクラスは以前の実験で用いたphotoresistorと同様のBaseSensorクラスを用います。そのtask/sensorオブジェクトのアーキテクチャーはDACの出力電圧が安定するのに、また、LEDからの反射光を認識するためのIR検出器に必要なdelayに対して有利です。この赤外線システムは仮想周辺装置が全てバックグラウンド処理され、タイミングはきわどくないのでマルチタスクシステムでは実際にうまく動作します。

次のソースはIrRangeSensorクラスです。

IR Range Sensor Class
package JBot ;

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

/**
 * IR Range Sensor Class
 * 
 * IR range sensors support
 */

public class IrRangeSensor extends BaseSensor {
  protected IrRangeSensorTask sensorTask ;

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

  static final int noObstacle = 15 ;

  /**
   * IR range sensorオブジェクトを作り、タスクをサポートする
   *
   * 入力:int leftDacPin: DAC出力に用いるCPU.pin
   * 入力:int leftPwmPin: PWM出力に用いるCPU.pin
   * 入力:int rightDacPin: DAC出力に用いるCPU.pin
   * 入力:int rightPwmPin: PWM出力に用いるCPU.pin
   * 入力:int deadband: deadband制限
   */
  public IrRangeSensor ( int leftDacPin
                       , int leftPwmPin
                       , int leftDetectorPin
                       , int rightDacPin
                       , int rightPwmPin
                       , int rightDetectorPin
                       , int deadband ) {
    sensorTask = new IrRangeSensorTask
                       ( this
                       , leftDacPin
                       , leftPwmPin
                       , leftDetectorPin
                       , rightDacPin
                       , rightPwmPin
                       , rightDetectorPin ) ;
    this.deadband = deadband ;
  }


  /**
   * obstacleDetectedが呼び出し可能かどうか調べる
   *
   * 戻り:obstacleDetectedが呼び出し可能の場合trueを返す
   */
  public boolean ready () {
    sensorTask.checkSensors() ;

    return ready ;
  }

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

    return obstacleDetected ;
  }

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

  /**
   * 指定された方向における障害物までの距離を得る
   * none値は物体は検出されなかったことを意味する
   *
   * 入力:int direction:距離を求める方向
   *
   * 戻り:指定された方向における障害物までの距離
   */
  public int obstacleDistance ( int direction ) {
    return distance ;
  }

  /**
   * センサー情報に基づいた結果を更新する
   * Called by sensor task when results available.
   *
   * 入力:int: resultLeft: photoresistorの rcTimeの結果
   * 入力:int: resultRight: photoresistorの rcTimeの結果
   */
  protected void saveResults ( int resultLeft, int resultRight ) {
    // 現在の結果が有効
    // 障害物のステータスを保存する
    switch (   (( resultLeft < noObstacle ) ? 1 : 0 )
             + (( resultRight < noObstacle ) ? 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;
    }

    ready = true ;
    notify();
  }
}


IrRangeSensorクラスはPhotoresistorSensorクラスに非常に似ています。最新の障害物検出情報はセンサーのsaveResultメソッドを呼ぶことによってIrRangeSensorTaskによって更新されるオブジェクト変数で管理されています。noObstacle定数定義は何も物体が検出されない時に用います。IR検出器で変調されたIR光が検出されない時に、タスクによって戻される距離は15になります。これは障害物が検出された場合、その距離範囲は0~14であることを意味します。

この距離は特別な単位はありませんが、0は障害物が非常に近いか、J-Botに接触していることを意味します。以前の実験のように、deadbandの値は、左右の検出器からの距離値が接近のとき、物体がJ-Botの前にあるかどうかを決定するために用います。

IrRangeSensorのコンストラクタはIrRangeSensorTaskを作り、開始します。IrRangeSensorTaskのコンストラクタは各々のDAC,PWM,IR検出器の対に用いられている6つのピンを入力としています。勿論、タスクはセンサーオブジェクトと同様に参照される必要があります。

次のソースはIrRangeSensorTaskのクラスの定義です。

IR Range Sensor Class
package JBot ;

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

/**
 * IR Range Sensor Class
 * 
 * IrRangeSensorをサポートする
 * 他のオブジェクトから直接呼んではいけない
 */

public class IrRangeSensorTask extends Task {
  protected IrRangeSensor sensor ;

  protected PWM leftPwm ;
  protected DAC leftDac ;
  protected int leftDetectorPin ;

  protected PWM rightPwm ;
  protected DAC rightDac ;
  protected int rightDetectorPin ;

  protected PWM pwm ;
  protected DAC dac ;
  protected int detectorPin ;

  protected int iteration ;
  protected int range ;
  protected int leftResult ;

  final static int startChecking = 1 ;
  final static int checkLeftPin = 2 ;
  final static int checkRightPin = 3 ;

  protected IrRangeSensorTask
              ( IrRangeSensor sensor
              , int leftDacPin
              , int leftPwmPin
              , int leftDetectorPin
              , int rightDacPin
              , int rightPwmPin
              , int rightDetectorPin ) {
    this.sensor = sensor ;
    leftPwm  = new PWM(leftPwmPin,1,2);
    leftPwm.stop();
    leftDac  = new DAC(leftDacPin);
    leftDac.stop() ;
    this.leftDetectorPin = leftDetectorPin ;

    rightPwm = new PWM(rightPwmPin,1,2);
    rightPwm.stop();
    rightDac = new DAC(rightDacPin);
    rightDac.stop();
    this.rightDetectorPin = rightDetectorPin ;
  }


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

  /**
   * 電圧を変更し、delayを設定する
   */
  protected void changeVoltage () {
    dac.update(255 - (13*iteration));
    sleep(state,5);
  }

  /**
   * IRセンサーの動作を開始させる
   * checkRangeとchangeVoltageで用いるDAC,PWM,IR検出器のピンを保存する
   * 反復と距離カウンターをリセットする
   * PWMとDAC仮想周辺装置を開始する
   * 初期電圧とdelayを設定する
   *
   * 入力:DAC dac: DAC仮想周辺装置
   * 入力:PWM pwm: PWM仮想周辺装置
   * 入力:int detectorPin: IR検出器出力に接続されているピン
   * 入力:int nextState: delayの後の次の状態
   */
  protected void startPulse ( DAC dac, PWM pwm, int detectorPin, int nextState ) {
    iteration = 0 ;
    range = 0 ;

    this.dac = dac ;
    this.pwm = pwm ;
    this.detectorPin = detectorPin ;

    dac.start();
    pwm.start();
    nextState ( nextState ) ;
    changeVoltage () ;
  }

  /**
   * IR検出器をチェックする
   * チェックがされていなかった場合、電圧とdelayを変更する
   *
   * 戻り:チェックがされた場合 true
   */
  protected boolean checkRange () {
    if ( CPU.readPin ( detectorPin )) {
      ++ range ;
    }

    if ( iteration == 15 ) {
      // Done checking. Turn everything off
      pwm.stop();
      dac.stop();
      return true ;
    } else {
      // Update counter, change voltage and set delay
      ++ iteration ;
      changeVoltage () ;
      return false ;
    }
  }

  /**
   * マルチタスク・サポート
   */
  public void execute () {
    switch ( state ) {
    case startChecking:
      // 左のDAC,PWMを開始しdelayする
      startPulse ( leftDac, leftPwm, leftDetectorPin, checkLeftPin );
      break;

    case checkLeftPin:
      if ( checkRange ()) {
        // 結果を保存する
        leftResult = range ;

      // 右のDAC,PWMを開始しdelayする
        startPulse ( rightDac, rightPwm, rightDetectorPin, checkRightPin );
      }
      break;

    case checkRightPin:
      if ( ! checkRange ()) {
        // Delayを設定しメソッドを出る
        break;
      }

      // 結果を保存し、タスクを停止させる
      sensor.saveResults ( leftResult, range ) ;

    default:
    case initialState:
      stop() ;
      break;
    }
  }
}


IrRangeSensorTaskのコンストラクタは前の実験で行ったのと同じ方法で仮想周辺装置を割り当て、DACとPWMオブジェクトを停止させます。これらは必要な時に再開されます。

dac,pwm,detectorPin変数はタスクが片側かあるいは反対側とチェックするように管理されているので、現在アクティブなDAC,PWMとIR検出器のピンの値が入っています。これらの変数はchangeVoltage,startPulse,checkRangeメソッドによって用いられます。もしそのオブジェクト変数が用いられなかった場合、これらのメソッドは対応するパラメータとして必要になります。

executeメソッドは、前の実験のPhotoresisterSensorTaskに似た構造になっています。initialStateIrRangeSensorcheckSensorsメソッドによって再開されるタスクを停止させます。startChecking状態はcheckSensorsメソッドがタスクを再開させた時に入ります。この状態はrange,iterationカウンターをクリアし、dac,pwm,detectorPin変数を設定し、PWM,DACオブジェクトを開始し、DAC電圧を設定するstartPulseメソッドを呼び、安定化されるまでタスクをsleepさせます。このメソッドから戻った時に、仮想周辺装置はIR LEDを通して変調されたIR光を送るように構成させます。

checkLeftPin状態はタスクがsleepを終えた時に入ります。タスクはcheckLeftPin状態を存続しIR LEDが全ての16レベルの電圧で実行されるまでcheckRangeメソッドが繰り返し呼ばれます。checkRangeメソッドはIR検出器のピンをチェックし、もし光が検出された場合rangeカウンターを更新します。iteration変数はインクリメントします。全ての反復が実行され、DACとPWMが切断された時にcheckRangeメソッドはtrueを返します。この結果は、もう片方がチェックされるべき時かどうかを判断する時に用いられます。右側に対するstartPulseメソッドはこの時点で距離の値が保存された後にexecuteメソッドの中で呼ばれます。

右側の処理が繰り返されます。checkRightPin状態はcheckRangeメソッドを用いますが、initialStatestopタスクメソッドはdouble dutyをするために呼ばれ、checkRangeがfalseを返した後で停止します。この時点で、保存されているleftResultと右側からの現在のrangeを用いて、センサーのsaveResultメソッドが呼ばれます。

IR range sensorオブジェクトをテストすることは、次のプログラムを用いることによって比較的簡単にできます。

IR Range Sensorテスト
import stamp.core.*;
import stamp.util.os.* ;

import JBot.*;

/**
 * IR Range Sensor Test
 */

public class IrRangeSensorTest1 extends Task {
  protected IrRangeSensor sensor =
    new IrRangeSensor ( CPU.pin7    // left DAC pin
                      , CPU.pin1    // left PWM pin
                      , CPU.pin0    // left detector pin
                      , CPU.pin8    // right DAC pin
                      , CPU.pin3    // right PWM pin
                      , CPU.pin2    // right detector pin
                      , 2           // deadband
                      ) ;

  public void execute() {
    if ( sensor.obstacleDetected ()) {
      int direction = sensor.obstacleDirection () ;

      System.out.print   ( "Dir=" ) ;
      System.out.print   ( direction ) ;
      System.out.print   ( "   Range=" ) ;
      System.out.println ( sensor.obstacleDistance (direction)) ;
    } else {
      System.out.println ( "none" ) ;
    }

    sleep(state,200);
  }


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

    Task.TaskManager();
    System.out.println ( "Should not have terminated!" ) ;
  }

}


テストプログラムはIRセンサーを順々に作るタスクを作ります。IrRangeSensorTest1executeメソッドは単純にセンサーをポーリングして現在のステータスを表示します。もし、その情報がとても早く表示される時は、付加的な状態を付け加えて、ステータスが表示される前後にsleepさせることができます。

実験7-3 障害物検出と回避

IR検出器について興味あることはその出力が触覚(whisker)のようであることです。主な違いは触覚は障害物に接触したことを報告し、IRではJ-Botが障害物に接触する前に回避するように距離を知ることができます。

触覚プログラムをIR障害物検出/回避に変換する

IrRAngeSensorを用いて触覚障害物回避プログラム(AvoidObstacleTaskWhiskerTest1)を変更することは、全ての仕事がAvoidObstacleTaskですでに操作されているので非常に簡単です。このオブジェクトはコンストラクタの引数としてセンサーオブジェクトを持っていきます。穴埋め問題と似たようなものです。

AvoidObstacleTaskクラスをテストする
import stamp.core.*;
import stamp.util.os.* ;
import JBot.* ;

/**
 * AvoidObstacleTaskクラスをテストする
 * 
 * 障害物を避けてJ-Botを回転させる
 */

public class AvoidObstacleTaskIrRangeTest1 {
  public static void main () {
    new AvoidObstacleTask
      ( new IrRangeSensor ( CPU.pin7    // left DAC pin
                          , CPU.pin1    // left PWM pin
                          , CPU.pin0    // left detector pin
                          , CPU.pin8    // right DAC pin
                          , CPU.pin3    // right PWM pin
                          , CPU.pin2    // right detector pin
                          , 1           // deadband
                          )
      , new RampingJBot ( new MultitaskingJBot ())) ;

    Task.TaskManager () ;
    System.out.println ( "All done" ) ;
  }
}


IR対に変更された触覚障害物回避プログラムがいかに放浪するか

実際には殆ど変更はされていません。AvoidObstacleTaskは新しく作られたIrRangeSensorオブジェクトを開始します。J-Botが連続的に前進している間に物体が検出されたかどうかを知るためにポーリングします。そのタスクはすでに全ての動作コントロールを用意しています。

deadbandの値を調節することは可能です。値を0に設定することはもし直前に物体を検出したならばJ-Botは後退するだけという意味になります。即ち、両方のセンサーが同じ距離を返します。値を2に増やすことはJ-Botを少し鈍感にして、検出をより寛容にします。

実験7-4 落下検出器

IR検出器とLEDは床に対して平行にねらいをつけています。これはJ-Botが直前のもの、あるいは僅かに横のものを検出できるようにしています。これらを床方向に照準を合わせることによって、テーブルの端のようなところで落下が起こるかどうかを知ることができます。

障害物回避のAvoidObstacleTaskの動作を落下回避に再利用することができる多くのアプローチがあります。1つは僅かに異なった仕事を行うAvoidObstacleTaskのような新しいクラスを作り出すことです。前に障害物が検出されても前進します。

もう1つの方法は障害物として落下を知らせる新しいセンサーオブジェクトを作り出すことです。AvoidObstacleTaskを変更するのは少し難しいのでこの方法を用いることにします。新しいセンサーオブジェクトを作ることは単純にIrRangeSensorオブジェクトを拡張することです。

落下を検出するIR Range Sensorクラス
package JBot ;

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

/**
 * 落下を検出するIR Range Sensorクラス
 * 
 * IR range sensors support
 */

public class IrRangeDropOffSensor extends IrRangeSensor {
  private int direction ;
  private int threshold ;

  /**
   * IR range sensorオブジェクトを作り、タスクをサポートする
   *
   * 入力:int leftDacPin: DAC出力に用いるCPU.pin
   * 入力:int leftPwmPin: PWM出力に用いるCPU.pin
   * 入力:int rightDacPin: DAC出力に用いるCPU.pin
   * 入力:int rightPwmPin: PWM出力に用いるCPU.pin
   * 入力:int deadband: deadband制限
   */
  public IrRangeDropOffSensor
                       ( int leftDacPin
                       , int leftPwmPin
                       , int leftDetectorPin
                       , int rightDacPin
                       , int rightPwmPin
                       , int rightDetectorPin
                       , int deadband ) {
    super ( leftDacPin
          , leftPwmPin
          , leftDetectorPin
          , rightDacPin
          , rightPwmPin
          , rightDetectorPin
          , deadband ) ;

    // J-Botは落下を見ていないと仮定する

    if ( super.obstacleDetected ()) {
      threshold = 10 +
        super.obstacleDistance ( super.obstacleDirection ()) ;
    } else {
      // Use default otherwise

      threshold = 20 ;
    }
  }


  /**
   * obstacleDetectedが呼び出し可能かどうか調べる
   *
   * 戻り:obstacleDetectedが呼び出し可能の場合trueを返す
   */
  public boolean obstacleDetected () {
    int distance ;

    // 障害物のステータスは普通のセンサーと逆

    if ( super.obstacleDetected ()) {
      direction = super.obstacleDirection () ;
      distance  = super.obstacleDistance ( direction ) ;

      // 落下が検出されない場合、距離は閾値よりも低い
      if ( distance > threshold ) {
        // 障害物の方向は下を見ている時は逆
        switch ( direction ) {
        case left:
          direction = right ;
          break;

        case right:
          direction = left ;
          break;

        default:
          direction = front ;
          break;
        }

        return true ;
      }
    }

    return false ;
  }

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

  /**
   * 指定された方向における障害物までの距離を得る
   * none値は物体は検出されなかったことを意味する
   *
   * 入力:int direction:距離を求める方向
   *
   * 戻り:指定された方向における障害物までの距離
   */
  public int obstacleDistance ( int direction ) {
    return 0 ;
  }
}


次のプログラムのように、このオブジェクトと普通のAvoidObstacleTaskオブジェクトを組み合わせれば全てが動作します。

AvoidObstacleTaskクラスをテストする
import stamp.core.*;
import stamp.util.os.* ;
import JBot.* ;

/**
 * AvoidObstacleTaskクラスをテストする
 * 
 * 障害物を避けてJ-Botを回転させる
 */

public class AvoidDropOffTaskIrRangeTest1 {
  public static void main () {
    new AvoidObstacleTask
      ( new IrRangeDropOffSensor
                        ( CPU.pin7    // left DAC pin
                        , CPU.pin1    // left PWM pin
                        , CPU.pin0    // left detector pin
                        , CPU.pin8    // right DAC pin
                        , CPU.pin3    // right PWM pin
                        , CPU.pin2    // right detector pin
                        , 1 )           // deadband
      , new RampingJBot ( new MultitaskingJBot ())) ;

    Task.TaskManager () ;
    System.out.println ( "All done" ) ;
  }
}


AvoidObstacleTaskオブジェクトは新しいIrRangeDropOffSensorオブジェクトを用います。IrRangeDropOffSensorが何も検出しない時にセンサーは障害物を検出するようにしています。

課題7-4-1

  1. ここで示したアプローチではもしJ-Botが周りに低い突起があるようなテーブル上で走っている場合、少量の垂直方向の違いを検出できないことが起こります。これをどのようにして克服するか?
     ヒント:AvoidObstacleTaskクラスを置き換えて、距離の変化をチェックする新しいクラスを作る。

  2. 新しいクラスを用いて、落下と障害物の両方をチェックしなさい。ヒント:距離の変化が正のときに落下が起こります。変化が負の時に障害物が検出することができます。


問題7

  1. 赤外線の意味は何か? 近赤外線とどのように異なるのか?

  2. J-Botに付属しているIR検出器組み込まれている2種類のフィルタはどういう種類のものか? 各々何をするか?

  3. 2個のIR検出器の出力の意味することを説明しなさい。

  4. 赤外線距離センサーを作るときに用いたDACとPWM仮想周辺は何故用いたか?

  5. 左のIR対の前に右のIR対をチェックするようにIrRangeSensorを修正しなさい。

  6. IrRangeSensorオブジェクトはcmの単位で補正されていません。


  7. IR物体検出の欠点の一つはJ-BotのIR検出器は黒を検出しないことです。これは黒はIRを反射する代わりに吸収するためです。J-BotはIRでうろついている時に黒い物体にぶつかる傾向がありますが、これはそれらが見えないからです。ブレッドボードに触覚を付け、BaseSensorを拡張したセンサークラスを作り、IrRangeSensorWhiskerSensorオブジェクトを用いなさい。
    ヒント:新しいセンサークラスのオブジェクトメソッドは触覚センサーを始めにチェックし、次にIrRangeSensorをチェックします。


実験7終わり