レゴブロックでwebから操作できるカメラ付きのロボットを作る!
今年で3年目に突入した計算数学応用コースの越冬プロジェクトです。
マインドストームズという、ロボットを作ってそれをプログラムできるレゴブロックのセットがあります。トランジスタ技術(2002年8月号)の特集を参考に、それを使ってロボットを作ってさらにカメラをつけてカメラ付きロボット全体をwebから制御できるようにしてしまおうというよくばりな計画です。
blogをみる
カメラ部分については去年までにできていて、昔の携帯のカメラをLinuxの乗ったマイクロコンピュータ(以下マイコン)で処理して、無線LANカード経由でwebから画像を見れるようになっていました。今年はロボットの胴体部分(と、それを動かすソフトウェア)を作ることが残っていました。
参考にしているトランジスタ技術の記事ではモーターを駆動するための回路も自作していますが、その方法だと回路をくむのが大変だし、後から変更を加えたくなったときにも融通が利きません。マインドストームズについてくるマイコンユニットが使えるならば、ロボットの胴体を作るのは楽だし後から変更を加えるのもとても簡単です。唯一の難点は外からの指示を伝える手段が限られていることです。
マインドストームズのマイコンユニットに外から指示を伝えるためには、押しボタンスイッチを使うという方法と、赤外線でメッセージを送るという方法があります。人間にとっては赤外線を出すよりはスイッチを押す方がずっと簡単ですが、手がついていないLinuxマイコンにとっては赤外線を飛ばすタイミングに合わせて電気信号を出すようにする方が簡単です。そこで、マインドストームズのマイコンユニットに赤外線でコマンドを送る方法をとることにしました。
システム概観
L-Card+ 16M
HTTPサーバーとして機能し、ロボット操作用のwebページ、アップレットを提供するほか、以下のコンポネントを持っています。
- 無線LANカード:メルコ製
- Trevaカメラユニット:PHS用のカメラユニットです。
- 赤外線発信回路:L-CardのCPUからはシリアルポート2(/dev/ttyS2)に見えています。シリアルポートからの入力を38kHz搬送波の赤外光に変換します。
- フリーラン発振で搬送波を生成:NE555Pをフリーラン発振させて38kHzの搬送波を作ります。
周波数 = 1.49 / (RA + 2RB)C = 1.49 / ((200 + 2 * 19.5k) * 1n) = 38k
デューティー比はほぼ1にしてありますが、本当はRA > 1kΩが望ましいようです。
- シリアル信号と搬送波を統合:搬送波とシリアル信号をまとめるためと、LED(後述)のバッファとしてSN74LS40Nを使用しています。
- 赤外光とインディケーター発光:直列につないだ赤外LEDと可視光(赤色)LEDを5Vでつってあります。下流はSN74LS40Nの出力端子につながっていて、これのレベルがLOWのときに発光します。

LEGO Mindstorms
RCXユニットL-Cardからの赤外コマンドに応じてロボットの車体を駆動(モーター制御)します。
モーター
L-CardとRCXユニットとの間の通信
L-Cardが送信側でRCXユニットが受信側の、一方向通信で、赤外線を使用します。送信側は自作の赤外光発光回路を使用し、受信側はユニットについている赤外通信機能を利用します。
マインドストームズマイコンの赤外通信プロトコル
ビットレベルでは、2400bps、奇数パリティ、1ストップビットのシリアル信号が38kHzに乗っている赤外光パターンが有効です。
パケットレベルでは、それぞれのメッセージが
0x55 0xff 0x00 D1 ~D1 D2 ~D2 ... Dn ~Dn C ~C (Diたちがデータ、C = D1 + D2 + ... + Dn、~はビット反転)
という構造のパケットにのせられて伝えられます。(LEGO MINDSTORMS Internals by Russell Nelson参照)この規格にあったシリアル信号をこの回路に送り込めばマインドストームズマイコンに情報を伝えることができます。例えば"F7 01"というデータ("1"を伝えるメッセージ)を送りたかったら
0x55 0xff 0x00 0xf7 0x08 0x01 0xfe 0xf8 0x07
を表す2400bps、奇数パリティ、1ストップビットのシリアル信号を38kHzで振動させたパターンで赤外光を発光することになります。
パソコン
ユーザーはLAN経由でL-Cardのロボット操作用webページにアクセスし、ダウンロードしたアップレットを通じてロボットを操作します。
ソフトウェア
このロボットを動かすために4つのプログラムが動いています。
今のところ、カメラドライバ、cgiスクリプト、コマンド中継プログラム(myir-cross)はブートのたびに/tmp/にダウンロードするようにしています。カメラドライバ(treva.o)はinsmodコマンドでシステムに登録します。カメラのデバイスファイル(メジャーデバイスナンバー20)は/dev/trevaを作ってあるのでmknodは必要ありません。カメラにアクセスするときはこのファイルを使ってください。
iscptという名前のシェルスクリプトにこのファイルたちをwgetして実行権限などを設定するコマンドを並べてあります。
LEGOCmd.class, ImageView.class
このアップレットの役割は ロボットを操作するためのユーザーインターフェースを提供し、Trevaカメラの画像を表示することです。
場所:L-Cardファイルシステムの/usr/ghttpd/httdoc
このJAVAアップレットの動作:起動時にL-Card上のホストプログラムとのコミニュケーション (インターネットソケット#1234) を確立します。ユーザーの操作に応じてホストプログラムにメッセージを送ります。
L-CardのHTTPサービスのルートディレクトリにあるtest.htmlというwebページに埋め込んであります。ユーザーはこのページ(http://####/test.html:####はL-Cardのアドレス)にアクセスしてアップレットをダウンロード・実行します。
ソースファイル:LEGOCmd.java
LEGOCmd.javaのpseudo code
LEGOCmd.class
initメソッド
- ホスト(L-Card)のURLを取得
- ホストの1234ポートのソケットを開く
- コントロールボタン用のコンテナを作成
- コントロールボタン(左旋回・走行・画像停止・走行停止・右旋回)の作成と初期化
- コントロールボタンコンテナを自分の下部に追加
- カメラ画像表示用のImageViewインスタンスの作成、画像をセット
- ImageViewインスタンスを自分の中央に追加
- スレッドオブジェクトの作成
- 画像停止フラグをfalse(画像を更新)に設定
startメソッド
- スレッドをスタート
runメソッド
- 画像更新のためのint変数を初期化(URLを一こまごとにかえることでキャッシュを使われないようにする)
- 以下の手順を無限ループ
- 画像停止フラグがtrueのとき
- ホスト(L-Card)のカメラCGIを呼んで新しい画像を取得
- MediatTrackerオブジェクトを作成、カメラ画像をセット
- カメラ画像が読み込まれるまで待つ
- ImageViewオブジェクトに新しいカメラ画像をセット
- ImageViewオブジェクトをrepaint
- 1秒待機
actionPerformedメソッド(コントロールボタンから呼ばれる)
- 画像停止ボタンだったら自分のflipMotionメソッドへ
- その他のボタンだったら
- コマンド文字列(01\n~04\n)を作成
- ソケットが使用可能だったらコマンド文字列を送信
flipMotionメソッド
- 画像停止フラグを反転
ImageView.class(画像表示コンポネント)
setImageメソッド
- 表示イメージを設定
paintメソッド
- イメージを72pix × 96pixサイズで自分の中央に描画
test.html
LEGOCmdアップレットが埋め込んであるHTMLファイルです。L-Cardの/usr/ghttpd/httdoc/にあります。
myir-cross
L-Card上のホストプログラムはユーザーインターフェース(JAVAアップレット)からのメッセージを下に車体制御マイコンに指示を送ります。
場所:WinマシンのC:\ftpd\
L-Card起動後にftpでL-Cardファイルシステムの/tmp/にダウンロードして使用します。
このCプログラムの動作:起動時にシリアルポート2(赤外発信回路)のプロトコル設定と、JAVAアップレットがアクセスするためのコミュニケーションポート (#1234)の作成を行います。JAVAアップレットから届くメッセージに応じて、マインドストームズマイコンのためのコマンドパケットを作成し、シリアルポート2から送信します。
ソースファイル:myir.h, myir.c
myir.hでの宣言
- 送受信バッファの長さ:256
- シリアルポート (ch.2) のデバイスファイルのパス:/dev/ttyS2
- インターネットソケットのポート番号:1234
- エラーのときに返す値:-1
myir.cのpseudo code
- プログラム起動メッセージを表示
- 赤外コマンドパケットのひな形(メッセージ「1」)を作成
- シリアルポートのデバイスファイルをオープン
- シリアルポートの状態をダミーリード
- termios構造体変数の初期化
- ボーレートを出力/入力とも2400に設定
- パリティチェックを有効、奇数パリティに設定
- シリアルポートを設定
- メッセージ「1」を(試しに)送信
- インターネットドメインにソケットを作成
- ソケットの設定
- 受信開始 (listen)、クライアントから接続されるまでブロック (accept)
- クライアントが接続してきたらソケットに関連づけられたファイルストリームを作成
- メッセージを受信するまでブロック
- 受信したメッセージデータを取得
- メッセージの先頭部分が表している数値を取得
- 赤外コマンドパケットの作成
- パケットをシリアルポートに出力
- EOFを受信するまで「メッセージを受信するまでブロック」から繰り返し
- EOFを受信したらソケットを閉じる
- シリアルポートのデバイスファイルを閉じる
- 0を返して終了
treva.o
L-Card上のカメラドライバはTrevaカメラの画像データを取得します。
場所:WinマシンのC:\ftpd\
L-Card起動後にftpでL-Cardファイルシステムの/tmp/にダウンロードして使用します。
「トランジスタ技術」2002年8月号の付録をそのまま使っています。
trejpeg.cgi
L-Card上のCGIスクリプトはTrevaカメラの画像データをJPEG形式に変換してクライアントに提供します。
場所:WinマシンのC:\ftpd\
L-Card起動後にftpでL-Cardファイルシステムの/tmp/にダウンロードして使用します。
このCプログラムの動作:アクセスに応じてカメラのデータを取得し、それをJPEG形式に変換し、ヘッダをつけて出力します。
JPEG形式への変換はjpeglibライブラリを利用しています。
「トランジスタ技術」2002年8月号の付録をそのまま使っています。
filename.ext?
RCXユニット上のロボット制御プログラムはコマンドに応じて車体の動きを制御します。
制御プログラムはRCXユニットのプログラムスロット5に入っています。
このRCXプログラムの動作:起動したら赤外コマンドを受信するまで待ちます。L-Cardからの赤外コマンドを受信したら、その内容に応じて各モーターの回転方向とパワーON/OFFを設定し、次のコマンドを待ちます。
ソースファイル:filename.ext
filename.extのpseudo code
- 赤外メッセージのクリア
- 各モーターのパワーを設定
- 赤外メッセージ受信までブロッック
- L-Cardからの赤外コマンドを受信したら、そのデータに応じて分岐
- データ = 1 : モーターAとモーターCを正方向に回転
- データ = 2 : モーターAは正方向、モーターCは逆方向に回転
- データ = 3 : モーターAは逆方向、モーターCは正方向に回転
- データ = 4 : モーターAとモーターCを停止
- 赤外メッセージをクリア
- 「赤外メッセージ受信までブロック」から繰り返し
開発
開発環境の構築については「トランジスタ技術」2002年8月号も参考にしてください。
L-Card
PCとシリアルケーブルでつないで、ターミナルプログラム(Tera Term)でログインします。ターミナルプログラムの設定についてはトラ技を参照してください。今のところユーザーは「root」(パスワード「laser5」)のみが設定されています。
LinuxがインストールされているのでLinux標準に準じた各種設定ファイルが有効で、ファイル操作や各種設定には普通のLinux(UNIX)コマンドが使えます。また、viエディタも入っているので最低限のテキスト編集もできます。ネットワーク関係の状態はifconfigコマンドで確認できます。
Cプログラムの開発
CプログラムはPC上のUNIXエミュレータ(Cygwin)上で作成し、クロスコンパイルしてL-Card用の実行ファイルを作ってから、FTPでL-Cardにダウンロードします。mipsel-linux-gccコマンドでクロスコンパイル、mipsel-linux-stripでオブジェクトサイズ縮小ができます。
JACAプログラムの開発
JAVAアップレットのコンパイルには、WindowsにインストールされたJ2SEのツール(javacなど)をコマンドプロンプトから使用してください。こちらもPCで作ったクラスファイルをFTPでL-Cardにダウンロードします。L-CardにはJAVAの実行環境がないので、JAVAプログラムをL-Card上で実行することはできません。webページに埋め込むJAVAアップレットは、そのページのhtmlファイルと同じディレクトリに置けばユーザーがそれをダウンロードして実行できます。
JARフォーマットの障害
今のところアップレットをJARアーカイブにまとめるとうまく働きません。classファイルを個別に置くようにすればとりあえずこの問題を回避できます。
WEBサイト
webページやアップレットたちは/usr/ghttpd/httdoc/に、cgiスクリプトは/tmp/におくように設定されています。
作成したブログラムのダウンロード
FTPによるプログラム転送やLANアクセスにはEthernetケーブルか無線LANカードを使用します。どちらも本体に接続しておいてからブートすれば使用できるように設定してあります。設定を変えるときは/etc/init.d/rcSファイルをみてください。(→ファイルシステムの注意参照)PC側でFTPサーバープログラムを実行しておきます。Tera Termを通じて、書き込み可能なディレクトリ(/tmpなど)にcdしてからwgetコマンドを実行して、コンパイルされた実行ファイルをL-Cardにダウンロードします。
ダウンロードしたファイルの書き込み
L-Cardのファイルシステムはデフォルトでは、安全のため/tmp/以外は書き込み禁止になっています。
mount -o remount,rw /
でほかの場所に書き込むことができるようになります。
mount -o remount,ro /
で、また/tmp/以外は書き込み禁止になります。
ファイルの編集
/tmp/以外のところにあるファイルを変更するときは、直接そのファイルを編集するのではなく、一度/tmp/にコピーしてそこで編集してから(上の手順でリマウントして)元の場所にコピーし直すようにした方がいいです。こうすると、システム領域に傷がつくリスクを減らすことができます。一度、設定ファイルを直接viで編集中にシステムがダウン→再起動してしまったことがありました。特に電池で駆動しているときは電源が不安定になりがちなので気をつけてください。
Mindstorms RCXユニット
Mindstormsセットに付属の開発環境(Win用)をインストールして使います。
赤外メッセージを受け取る前に必ず「メッセージのクリア」ブロックをおくようにしてください。
この回路の構成要素は
- 入力されたRS232規格の信号を他のICが取り扱えるCMOS規格に変える部分(ADM232AAN使用)
- 搬送波を作るために38kHzのパルスを発信する部分(NE555P使用)
- 赤外通信プロトコル(0ビットやスタートビットのときに発光)にあうようにシリアル信号を逆転する部分(SN74LS40N使用)
- シリアル信号と搬送波パルスを合わせる部分(SN74LS40N使用)
- マインドストームズマイコンに信号を伝える赤外LEDとそれに同期して光る人の目用の赤色LED
- 電源供給部分(9Vアルカリ乾電池とそこらへんに転がっていたDC-DCコンバーター:型番が見えなくなってしまいました・・・)
です。LEDたちは5Vで釣っておいてSN74LS40Nをバッファとして使っています。つまり、SN74LS40Nの下流がLowになるときにLEDが光ります。0ビットのときに38kHzでぶった切った光を出したいので、1度CMOS信号を反転させておいてから38kHzパルスとNANDを取ります。
この回路にN88互換BASIC for Win95(XPでも動きました)とUSB-シリアルコンバーター(I-O DATA USB-RSAQ2)の組み合わせで2400baud、奇数パリティのシリアル信号を送り込みLEDを発光させて、あらかじめテストプログラムを入れておいたマインドストームズマイコンにメッセージが伝わることを確かめました。ただし、0xf7のシリアル信号はなぜか出力できないようだったので、別の形式(Mindstorms IR-communication by Stef Mientk参照)
0x55 0xff 0x00 0xd2 0x2d 0x00 0xff 0x00 0xff 0xd2 0x2d
0x55 0xff 0x00 0xd2 0x2d 0x00 0xff 0x01 0xfe 0xd3 0x2c
のパケットたちを送りました。
Revision History
- 第二稿(Sep 4 2004)
- 第一稿(Aug 28 2004)