1.ロボットの仕様
これから作るロボットの仕様は次の通りです。
- 2台のNXTを用いる。1台はロボット用、もう1台はリモコン用とする。
- 2台のNXTをBluetooth通信を用いて遠隔操作を行う。リモコンをマスターとし、ロボットをスレーブとする
- リモコンの操作とロボットの動きの対応は以下の図に示すようなものとする。
- リモコンにはプログラム中断用のタッチセンサを取り付ける。プログラムの中断はリモコンとロボットの両方で行う。
- リモコンとロボットの両方にLEDを取り付ける。このLEDはリモコンから操作があるときに点灯する。
- ロボットは左右のタイヤにモータを1つずつ使用したものとする。
- 1つのプログラムでマスター用とスレーブ用を切り替えられるようにする。
2.ロボットの作成
まず、リモコンの作成をしましょう。
タッチセンサは入力ポート1に、LEDは出力ポートAに接続します。
次に、ロボットの作成をしましょう。
ロボットは左右それぞれにモータを使用するものであれば、どのようなものでも構いません。 ここでは、例として組立て図に記載されているロボットを用います。
通信確認用のLED部は次のように作成します。
LEDを出力ポートA、右のモータをポートB、左のモータをポートCに接続します。
【作り方】 ![]() ![]() ![]() ![]() ![]() ![]() |
次に、ロボットの作成をしましょう。
ロボットは左右それぞれにモータを使用するものであれば、どのようなものでも構いません。 ここでは、例として組立て図に記載されているロボットを用います。
通信確認用のLED部は次のように作成します。
【作り方】 ![]() ![]() ![]() ![]() ![]() |
3.プログラムの作成
それでは、プログラムの作成に移ります。
ここでは作成するプログラム、remocon.cとします。 またプログラムの調整や可読性の向上を図るため、様々な定数などを定義したヘッダを記述します。 ここではremocon.hとします。
このプログラムは比較的長いので、関数ごとに解説を行います。
まずはヘッダを見ていきます。
上記の通り、このヘッダでは様々な定数を定義してあります。
接続ポートをマクロにより定義することで、接続先が異なる場合であってもこの文を書き換えるだけで済みます。
走行モードとは、今回は送信する数字によって各種の動作の区別をするため、その際に用いる定数群です。
また、走行パワーや受信間隔を定義しました。この値は調整可能が望ましいため、マクロにしておくと良いでしょう。
さらにマクロだけでなく、スレーブ側(ロボット)の動作を定義しました。これにより、プログラムの可読性が上がると思います。
次にメインプログラムを見ていきます。
メインプログラムでは、動作させたNXTがマスター、スレーブ、Bluetooth接続していない、のどれに当たるかを判別しプログラムの分岐を行います。 このとき用いるのが、『nBTCurrentStreamIndex』という変数です。 この変数は現在、自分とBluetooth接続している機器の内、最もポート番号が少ないポートが格納されている変数です。 スレーブと接続していれば自身はマスターであり、マスターと接続していれば自身はスレーブであることが分かります。
このプログラムでは、スレーブ1〜3に接続していればそのポートを引数として、マスター用のプログラムを動作させます。 また、マスターに接続していればスレーブ用のプログラムを動作させます。 そして、Bluetooth接続がされていない場合はブザー音とともにプログラムが終了します(CheckBT関数は正しくない引数の場合、この動作をします)。
このようにすることで、動作の分岐だけでなくスレーブのどのポートに接続されている場合であってもプログラムを動作させることが可能です。 ただし、nBTCurrentStreamIndex変数を今回のように用いる場合は1対1の通信である必要があります。
次にマスター側(リモコン)のプログラムを見ていきます。
このプログラムは、メインプログラムからポートを引数で渡されて動作します。
送信や表示を一括して行うよう各種変数を用意しました。また、ボタンやタッチセンサを用いるのでそれらの初期化を行います。
タッチセンサはプログラムの終了なので、押されている場合を優先的に処理するために最初に処理を記述します。 この場合は、「LEDの点灯」「終了命令の送信」「LCDにProgram End!と表示」を行い、その2秒後にプログラムを終了します。
タッチセンサが押されていない場合は、ボタンの判別を行い、それにより次の表のように動作を分けます。
そして、これにより定められた数字の送信やLEDの点灯などを行います。
このセンサ・ボタンのチェック〜送信・表示までを50msec毎に何度も行います。
最後にスレーブ側(ロボット)のプログラムを見ていきます。
スレーブ側もマスターと同様に初期化から始まり、受信データによる各種動作を実行します。 また、スレーブ側は各種動作をヘッダに記述したため、プログラムが簡素で分かりやすくなっています。
ここでは作成するプログラム、remocon.cとします。 またプログラムの調整や可読性の向上を図るため、様々な定数などを定義したヘッダを記述します。 ここではremocon.hとします。
このプログラムは比較的長いので、関数ごとに解説を行います。
まずはヘッダを見ていきます。
#pragma platform(NXT) // ポート定義 #define MASTER_LED motorA #define SLAVE_LED motorA #define MOTOR_LEFT motorC #define MOTOR_RIGHT motorB // 走行モード定義 #define FORWD 10 #define LEFT 20 #define RIGHT 30 #define BACK 40 #define STOP 50 #define EXIT 100 // 走行パワー定義 #define FWD_POWER 75 #define VRT_POWER 30 // 受信時間間隔定義 #define CHECK_TIME 50 void go_forwd(){ motor[SLAVE_LED] = 100; motor[MOTOR_LEFT] = FWD_POWER; motor[MOTOR_RIGHT] = FWD_POWER; } void go_right(){ motor[SLAVE_LED] = 100; motor[MOTOR_LEFT] = VRT_POWER; motor[MOTOR_RIGHT] = -VRT_POWER; } void go_left(){ motor[SLAVE_LED] = 100; motor[MOTOR_LEFT] = -VRT_POWER; motor[MOTOR_RIGHT] = VRT_POWER; void go_back(){ motor[SLAVE_LED] = 100; motor[MOTOR_LEFT] = -FWD_POWER; motor[MOTOR_RIGHT] = -FWD_POWER; } void go_stop(){ motor[SLAVE_LED] = 0; motor[MOTOR_LEFT] = 0; motor[MOTOR_RIGHT] = 0; } void program_end(){ motor[SLAVE_LED] = 100; motor[MOTOR_LEFT] = 0; motor[MOTOR_RIGHT] = 0; wait1Msec(2000); StopAllTasks(); } |
上記の通り、このヘッダでは様々な定数を定義してあります。
接続ポートをマクロにより定義することで、接続先が異なる場合であってもこの文を書き換えるだけで済みます。
走行モードとは、今回は送信する数字によって各種の動作の区別をするため、その際に用いる定数群です。
また、走行パワーや受信間隔を定義しました。この値は調整可能が望ましいため、マクロにしておくと良いでしょう。
さらにマクロだけでなく、スレーブ側(ロボット)の動作を定義しました。これにより、プログラムの可読性が上がると思います。
次にメインプログラムを見ていきます。
task main(){ switch(nBTCurrentStreamIndex){ case SLAVE1: case SLAVE2: case SLAVE3: MasterOp(nBTCurrentStreamIndex); break; case MASTER: SlaveOp(); break; default: CheckBT(-1); break; } } |
メインプログラムでは、動作させたNXTがマスター、スレーブ、Bluetooth接続していない、のどれに当たるかを判別しプログラムの分岐を行います。 このとき用いるのが、『nBTCurrentStreamIndex』という変数です。 この変数は現在、自分とBluetooth接続している機器の内、最もポート番号が少ないポートが格納されている変数です。 スレーブと接続していれば自身はマスターであり、マスターと接続していれば自身はスレーブであることが分かります。
このプログラムでは、スレーブ1〜3に接続していればそのポートを引数として、マスター用のプログラムを動作させます。 また、マスターに接続していればスレーブ用のプログラムを動作させます。 そして、Bluetooth接続がされていない場合はブザー音とともにプログラムが終了します(CheckBT関数は正しくない引数の場合、この動作をします)。
このようにすることで、動作の分岐だけでなくスレーブのどのポートに接続されている場合であってもプログラムを動作させることが可能です。 ただし、nBTCurrentStreamIndex変数を今回のように用いる場合は1対1の通信である必要があります。
次にマスター側(リモコン)のプログラムを見ていきます。
void MasterOp(tBTPort SlavePort){ int mode; // 動作を代入する変数 int led_power; // LEDのパワーを代入する変数 string disp; // NXTのディスプレイに表示する文字 TButtons Button; // ボタン用変数 // センサやボタンの初期化 SetSensorTouch(S1); nNxtButtonTask = -2; nNxtExitClicks = -1; eraseDisplay(); nxtDisplayCenteredTextLine(1,"Now Sending"); motor[MASTER_LED] = 0; wait1Msec(100); while(true){ Button = nNxtButtonPressed; if(SensorValue(S1)){ // タッチセンサが押されている場合の処理 led_power = 100; SendBTMsg(SlavePort,EXIT); nxtDisplayCenterdTextLine(3,"Program End!"); wait1Msec(2000); StopAllTasks(); }else{ // 押されているボタンにより処理を区別 switch(Button){ case kLeftButton: // 左ボタン(左ターン) mode = LEFT; led_power = 100; disp = "Turn Left!"; break; case kRightButton: // 右ボタン(右ターン) mode = RIGHT; led_power = 100; disp = "Turn Right!"; break; case kEnterButton: // 中央ボタン(直進) mode = FORWD; led_power = 100; disp = "Go Forword!"; break; case kExitButton: // 下ボタン(後退) mode = BACK; led_power = 100; disp = "Back!!"; break; default: // ボタンなし(停止) mode = STOP; led_power = 0; disp = "Stop!!"; break; } } // LEDの点灯とスレーブへの送信 motor[MASTER_LED] = led_power; SendBTMsg(SlavePort,mode); // LCDへの表示 nxtDisplayClearTextLine(3); nxtDisplayCenteredTextLine(3,disp); wait1Msec(CHECK_TIME); } } |
このプログラムは、メインプログラムからポートを引数で渡されて動作します。
送信や表示を一括して行うよう各種変数を用意しました。また、ボタンやタッチセンサを用いるのでそれらの初期化を行います。
タッチセンサはプログラムの終了なので、押されている場合を優先的に処理するために最初に処理を記述します。 この場合は、「LEDの点灯」「終了命令の送信」「LCDにProgram End!と表示」を行い、その2秒後にプログラムを終了します。
タッチセンサが押されていない場合は、ボタンの判別を行い、それにより次の表のように動作を分けます。
ボタンの種類 | 動作内容 (mode変数) |
LEDの点灯 (led_power変数) |
LCDの表示内容 (disp変数) |
---|---|---|---|
左ボタン | LEFT(左ターン) | 点灯する | Turn Left! |
右ボタン | RIGHT(右ターン) | 点灯する | Turn Right! |
中央ボタン | FORWD(前進) | 点灯する | Go Forword! |
下ボタン | BACK(後退) | 点灯する | Back!! |
ボタンなし | STOP(停止) | 点灯しない | Stop!! |
そして、これにより定められた数字の送信やLEDの点灯などを行います。
このセンサ・ボタンのチェック〜送信・表示までを50msec毎に何度も行います。
最後にスレーブ側(ロボット)のプログラムを見ていきます。
void SlaveOp(){ // 受信用変数の定義・初期化 int data = 0; // LCDとLEDの初期化 eraseDisplay(); nxtDisplayCenteredTextLine(1,"Now Recieve"); motor[SLAVE_LED] = 0; while(true){ // データの受信とその表示 data = RecieveBTMsg(); nxtDisplayCenteredTextLine(2,"%d",data); nxtDisplayClearTextLine(3); // 受信データにより処理分け // 各処理の内容はヘッダに記述されている switch(data) { case FORWD: nxtDisplayCenteredTextLine(3,"Go Forword!!"); go_forwd(); break; case BACK: nxtDisplayCenteredTextLine(3,"Go Back!!"); go_back(); break; case LEFT: nxtDisplayCenteredTextLine(3,"Go Left!!"); go_left(); break; case RIGHT: nxtDisplayCenteredTextLine(3,"Go Right!!"); go_right(); break; case EXIT: nxtDisplayCenteredTextLine(3,"Program End!!"); program_end(); break; default: nxtDisplayCenteredTextLine(3,"Stop!!"); go_stop(); break; } wait1Msec(CHECK_TIME); } } |
スレーブ側もマスターと同様に初期化から始まり、受信データによる各種動作を実行します。 また、スレーブ側は各種動作をヘッダに記述したため、プログラムが簡素で分かりやすくなっています。
4.プログラムの実行
それではBluetoothの接続を行い、プログラムを実行します。今回はBluetoothのどのポートに接続しても動作するようになっています。
以下に撮影した動画を示します。このように動作すれば成功です。
保存する場合はコチラ(avi形式:8.48MB)
以下に撮影した動画を示します。このように動作すれば成功です。
保存する場合はコチラ(avi形式:8.48MB)
5.更なる応用へ
今回作成したリモコンロボットをあくまでも基礎的な部分のみであるので、十分に改良の余地があります。
一つはロボットの改良です。タイヤ駆動用とステアリング用のモータとするロボットに改造すること、 前項のホッケーロボットを用いることなどが挙げられます。
また、リモコンを改良し、変速用のスイッチ等を取り付けてロボットの変速を行うなどが挙げられます。
それぞれハードウェアやソフトウェアの多くの知識を要しますが、やりがいがあると思います。
一つはロボットの改良です。タイヤ駆動用とステアリング用のモータとするロボットに改造すること、 前項のホッケーロボットを用いることなどが挙げられます。
また、リモコンを改良し、変速用のスイッチ等を取り付けてロボットの変速を行うなどが挙げられます。
それぞれハードウェアやソフトウェアの多くの知識を要しますが、やりがいがあると思います。