これまでは,RCXとNQCを使ってモーターで動くロボットだけを作ってきましたが,この章ではRCXのTimer機能や音楽を鳴らす機能などを使って少し変わったものを作ってみましょう。


8-1.タイマーとサウンド,そしてミュージック

 RCXはTimer(タイマー)を内蔵していますので,時間を計ることができます。入力ポートと出力ポートは,それぞれ3つでしたが,Timerは4つあります(0,1,2,3)。Timerを使うにはClearTimer命令で使用したいTimerを初期化しTimer命令で値を取り出します。Timerの単位は1/10秒です。Waitの単位は1/100秒でしたね。間違えないようにしましょう。

サウンドを鳴らすPlaySound命令とともに使ってキッチンタイマーを作ってみましょう。

 timer1.nqc

 task main()
 {
  ClearTimer(0);
  until (Timer(0) > 100);
  PlaySound(SOUND_CLICK);
 }  

 10秒経過するとClick(クリック)音がなります。その他にも,組み込み定数を使っていろいろな音を鳴らすことができます。

 task main()
 {
  ClearTimer(0);
  until (Timer(0) > 100);
  PlaySound(SOUND_CLICK);
  until (Timer(0) > 200);
  PlaySound(SOUND_DOUBLE_BEEP);
  until (Timer(0) > 300);
  PlaySound(SOUND_DOWN);
  until (Timer(0) > 400);
  PlaySound(SOUND_UP);
  until (Timer(0) > 500);
  PlaySound(SOUND_LOW_BEEP);
  until (Timer(0) > 600);
  PlaySound(SOUND_FAST_UP);
 }  

  上記のプログラムでは10秒ごとに違う音がなります。最初はピッという音(SOUND_CLICK), 2回目はピー,ピーという音(SOUND_DOUBLE_BEEP),3回目はピロピロピロと音が下がっていき(SOUND_DOWN),4回目のSOUND_UPはその逆で音が上がっていきます。5回目は低いビープ音(SOUND_LOW_BEEP)で,6回目は3回目と同じ音階がもっと速く鳴ります(SOUND_FAST_UP)。

 RCX Command CenterのツールメニューにRCX Pianoというツールがあります。

 鍵盤をマウスでクリックすると,RCXが音符を鳴らします。NQCで音楽を鳴らすには,PlayTone命令を使います。PlayTone命令には2つの引数を指定します。1つめは音の高さ,2つめは,音の長さを1/100秒単位で指定します。音の高さは,周波数で表します。基準となるA(ラ)の音が440Hz(ヘルツ)です。

 plsound1.nqc

 task main() 
 {
  PlayTone(523,40);
  Wait(50);
  PlayTone(587,40); 
  Wait(50);
  PlayTone(659,40);
  Wait(50);
  PlayTone(698,40);
  Wait(50);
  PlayTone(659,40);
  Wait(50);
  PlayTone(587,40);
  Wait(50);
  PlayTone(523,40);
  Wait(50);
  }  

 実行すると「カエルのうたが・・・」が聞こえてきます。PlayTone命令は2つめの引数に指定した時間,音を鳴らしますが,実行(音を鳴らすこと)の完了を待たず,次の命令の処理を実行しますので,Wait命令で次の実行に移らないようにしています。
 よく使うであろう音と周波数の対応をA音(440Hz)を中心に記します。

 

低い

 
C(ド) 262 523
C#(ド#) 277 554
D(レ) 294 587
D#(レ#) 311 622
E(ミ) 330 659
F(ファ) 349 698
F#(ファ#) 370 740
G(ソ) 392 784
G#(ソ#) 415 831
A(ラ) 440 880
A#(ラ#) 466 932
B(シ) 494 988
    高い

 周波数で音を指定するのは,面倒だし間違えやすいです。次のように音を定数定義して使うのも良い方法です。

 #define C2 523
 #define CS2 554
 #define D2 587
 #define DS2 622
 #define E2 659
 #define F2 698
 #define FS2 740
 #define G2 784
 #define GS2 831
 #define A2 880
 #define AS2 932
 #define B2 988

 task main()
 {
  PlayTone(C2,40);
  Wait(50);
  PlayTone(D2,40);
  Wait(50);
  PlayTone(E2,40);
  Wait(50);
  PlayTone(F2,40);
  Wait(50);
  PlayTone(E2,40);
  Wait(50);
  PlayTone(D2,40);
  Wait(50);
  PlayTone(C2,40);
  Wait(50);
 }


8-2.乱数

 今まで作ってきたロボットはすべてプログラム通りの規則的な動作をしました。ロボットですから,プログラム通りに動くのは当たり前ですが,例えば,ペットロボットを作リたいとしましょう。規則的な動作しかできないと面白みに欠けますね。動物は皆,不規則な動きをします。乱数により,ロボットの動きに意外性を持たせてみましょう。

 乱数とは,サイコロのようなものです。振ってみるまで,どんな目が出るか分かりません。1つのサイコロを振ると1~6の目が出ます。これと同じことがRandom命令で実現できます。Random(5)とすると0~5のいずれかの値を返します。右に曲がるか,左に曲がるをRandom命令で決めることにしましょう。Random(1)の返す値が0だと左に,1だと右に曲がるようにします。それから,真っ直ぐ進む時間と曲がる時間にも乱数を使用します。ロボットが自分の意志で動いているように見えれば,プログラミング成功です。

 random.nqc

 int direct,time_st,time_tn;
 task main( )
 {
  while(true)
  {
   SetPower(OUT_A+OUT_C,OUT_HALF);
   time_st = Random(100);
   time_tn = Random(100);
   direct =Random(1);
   OnFwd(OUT_A+OUT_C);
   Wait(time_st);
   if (direct == 0)
   {
    OnRev(OUT_A);
   }
   else
   {
    OnRev(OUT_C);
   }
   Wait(time_tn);
  }
 }

  無限軌道車で試してみましょう。while命令の条件式にTrue(真)を指定していますので,一旦スイッチを入れるとロボットは永遠に走り回ります。direct = Random(1)で曲がる方向を決め,time_st = Random(100),とtime_tn = Random(100)で真っ直ぐ進む時間と曲がる時間を指定しています。

  Random命令は呼び出されるたびに違う値を返すので,ロボットはあちこち動きまわります。でも,そのうちどこかにぶつかって止まってしまいますね。タッチセンサーを使って,何かにぶつかったらバックして曲がるようにすれば良いでしょう。


8-3.SendMessage命令  

 作成したプログラムをRCXにダウンロードするには,パソコンからIRタワー(赤外線通信装置)を介して,プログラムをRCXに送信します。RCXには赤外線を受信する機能があるから,プログラムを送ることができるわけです。RCXにはこのように赤外線を受信する機能だけではなく,送信する機能もあります。

 赤外線を送出するにはSendMessage命令を使います。SendMessage(n)でメッセージを送ることができます。nに0~255の数値を指定します。SendMessage命令を使えば,複数のRCXをシンクロナイズド・スイミングのチームのように動かすこともできます。指揮するRCXが発した数値をチームのメンバーが受け取り,同じ動きをするようにプログラミングすれば良いのです。

 RCXが送出する赤外線はライトセンサーで読み取ることができます。ここでは,この機能を使って商店の玄関にあるチャイム,ドアを開けて店内に入るとピンポーンとチャイムがなる仕組みを使ってみましょう。RCXが同じ強さの赤外線を出力していれば,RCXの近くに人がいるかいないかで ライトセンサーが読み取る反射光の値が異なります。

赤外線ポートと同じ向きにライトセンサーをつけます。

 chy.nqc

 int lastlevel;
 task send_signal()
 {
  while(true)
  {
   SendMessage(0);
   Wait(10);
  }
 }
 task check_signal()
 {
  while(true)
  {
   lastlevel = SENSOR_1;
   if(SENSOR_1 > lastlevel + 100)
   {
    PlaySound(3);
    Wait(100);
   }
  }
 }
 task main()
 {
  SetSensorType(SENSOR_1,SENSOR_TYPE_LIGHT);
  SetSensorMode(SENSOR_1,SENSOR_MODE_RAW);
  start send_signal;
  start check_signal;
 }

  このプログラムではsend_signal,check_signal,mainの3つのタスクを定義しています。
send_signalタスクではSendMessage(0)で赤外線を出力します。Wait(10)で0.1秒間待機させていますので,send_signalタスクは0.1秒ごとに赤外線を出力します。check_signal タスクでは,まずライトセンサーが返す値を,変数lastlevel に代入します。次にセンサー1の値を読み取った時にlastlevelに保存した値と100以上違っていたら,RCXの前を何か通ったと判断し,PlaySoundでサウンドを鳴らしています。

 部屋の入り口など,人の出入りが多いところにRCXを置いてテストしてみましょう。誰かが通ったらチャイムがなるはずです。もし,うまくいかなかったらlastlevel + 100の100の値を変更してみてください。部屋の明るさなどに影響されますから。また,あまり部屋の間口が広いと赤外線の反射が小さすぎて読み取れません。

 プログラムはmainタスクから開始されますので,まずSetSensorType命令が実行されます。入力ポート1に接続されたセンサーがライトセンサーであると指定しています。

 さて次のSetSensorModeは今回初めて登場した命令です。SetSensorMode命令はセンサーのモードを指定します。以前に作ったライトセンサーを使うプログラムではモードは指定しませんでした。指定しないとデフォルトのモードが使用されます。ライトセンサーの場合は,SENSOR_MODE_PERCENTがデフォルトのモードです。このモードではライトセンサーが読み取った値をPERCENT(パーセント)にして,つまり0~100の値に加工して返します。SENSOR_MODE_RAWは未加工の値を返すモードです。このモードでは0~1023の値をセンサーから取得することができます。つまりRAW(未加工)モードを使うとパーセントモードよりも,より繊細なセンサーとして使うことができるのです。ただし,ライトセンサーを未加工モードで使った場合,返される値は300から800の範囲内になるようです。

  タッチセンサーを未加工モードで使用すると,タッチセンサーが押されている時は,1023に近い値を返します。タッチセンサーのデフォルトモードはSENSOR_MODE_BOOL(0から1を返すモード)なので,モードを指定しないと,押されていない時は0を,押されている時は1を返すわけです。押している強さを判断したい時にSENSOR_MODE_RAWを使えば良いのです。


8-4.計算

  コンピュータは,古くから電子計算機と日本語訳されてきました。現在では,インターネットやCG(コンピュータグラフィックス)など,その利用用途は格段に広がりをみせています。しかし,それらの技術を支える根底では計算機としてコンピュータの機能がフルに活用されています。画面上で恐竜の画像を少し動かすだけでも,座標の位置を変更するために膨大な量の計算が行われています。

 RCXも小さなコンピュータですから,計算を行うことができます。 houkou = 1の = は,変数houkouに1を代入する代入演算子であると以前に説明しました。その他の計算を行う代入演算子を紹介します。

a += 5 += は右辺の値を左辺に足し算します。
a -= 5 -= は右辺の値を左辺から引き算します。
a *= 5 *= は右辺の値を左辺に掛け算します。
a /= 5 /= は右辺の値で左辺を割り算します。


 次は算術演算子です。

a++ ++はインクリメント演算子と呼ばれ,a++とした場合,aに1を足します。  
a-- -- はデクリメント演算子でa--とした場合,aから1を引きます。  
a + b aとbを足し算します。 使用例 x = a + b
a - b aからbを引きます。 使用例 x = a - b
a * b a とbを掛け算します。 使用例 x = a * b
a / b a をbで割ります。 使用例 x = a / b

 これらの演算子を使い,プログラム中で変数の値を変化させることで,ロボットに複雑な動きを実行させることができます。ぜひトライしてみてください。