NXTプログラミング(4)

次にモーターの制御である。こいつが思ったより難しい。
NxtCommunicatorのメソッドであるSetOutputStateを用いれば


nc.SetOutputState(
NxtMotorPort.PortA, // 出力ポート
(sbyte)power, // 出力パワー(0〜100)
NxtMotorMode.MotorOn | NxtMotorMode.Brake,
NxtMotorRegulationMode.Idle,
0,
NxtMotorRunState.Running,
0
);

とするだけでモーターは回転する。powerは0〜100である。いきなりフルパワーで回転させると大変なことになるといけないのでまずは25ぐらいで回してみよう。


LEGO MINDSTORMS NXTグレーブック―プログラムノツヅラ
問題なのは、このパラメータのそれぞれの意味である。NxtCommunicator.SetOutputStateの定義を追いかけて行ってもほとんどそのままbyte[]に格納してsendMessageしているだけなのでさっぱりわからない。


BDK*1のDirect Commandのp.6に簡単な説明があるのだが、やはりわからない。


実は、BDKではなく、Executable File and Bytecode Reference*2のpp.48-54に各フラグの詳しい説明がある。


グレーブックのほうには、p.157にモーターを動かす送信データとして、各パラメータの説明があるが、説明が不足している。この著者もおそらくBDKを見て、なんとなくわかったつもりになったのだろうが、「2が複数モーターの同期をとるための設定」とか書いてあっても、そのときにどうパラメータを指定して、どうやって同期をとればいいのかわからないし、+5バイト目の説明として「回転レート」とか書いてあっても、何と何の比率なのかさっぱりわからない。「回転レート」というのは、BDKに書いてあったTurn Ratioを意味もわからずに翻訳しただけなのではないか。


p.190の+5バイト目(こちらはsetOutputStateの説明なので表では+6バイト目になっている)の説明には「回転レート  加減速の設定値」という説明があるが、同じことを説明するのに一か所にまとめないのは本としての作りが良くない。


そもそも+5バイト目の意味することは、2つのモーターを同期させて動かすときの2つのモーターの回転の比率である。「回転レート」や「加減速の設定値」という言葉から、それが読み取れるだろうか?どう控え目に見ても著者自身、このパラメータの意味がわかっていないとしか思えない。


モーターを制御するという一番基本の部分に対して、それぞれのパラメータに対して、もっとしっかりした説明が欲しかった。


ついでに言えば、この本のなかに超音波センサーで距離を測定するプログラムのサンプルが無い。これはとても残念なことである。特に、この本のなかで製作されたベータレックスというNXTを2台使うロボットは、ソースを見る限り、P.179で「使用するモーターとセンサー」書かれている超音波センサーもサウンドセンサーも使っていない。とりつけてある超音波センサーもサウンドセンサーもただの飾りである。(飾りとして「使用している」と言いたいのだろうか?)


と批判ばかりになったが、NXTのプログラミング本は、日本にはこのグレーブックしか無いような状況なので、NXTでプログラミングをやるつもりなら、とりあえずは買っとけ!(`ω´) このベータレックスにしても相当の試行錯誤が必要な作品で、このような重量級の作品がNXT2台の連携で動いていること自体特筆すべきことだから。


ところで、私も、NXTが到着したばかりなので、このコマンドのすべてのパラメータの意味を正確に把握している自信は無い。以下に私が理解している範囲でNxtCommunicator.SetOutputStateの定義に説明を書いて掲載しておくので、間違いがあれば今日のコメント欄で教えてもらえるとありがたい。グレーブックにも以下の程度の説明は欲しかった。


/// <summary>
/// Sends a drive command to one of the motors
///
/// ひとつのモーターに対して制御するためのコマンドを送信する
/// </summary>
/// <param name="port">出力ポート</param>
/// <param name="power">回転パワー</param>
/// <param name="mode">モーターのモード</param>
/// <param name="regulationMode">レギュレーションモード</param>
/// <param name="turnRatio">モーターを同期させるときのつのモーターの回転の比率</param>
/// <param name="runState">走行状態</param>
/// <param name="tachoLimit">回転角度</param>
public void SetOutputState(NxtMotorPort port, sbyte power, NxtMotorMode mode,
NxtMotorRegulationMode regulationMode, sbyte turnRatio, NxtMotorRunState runState, uint tachoLimit) {

byte[] message = new byte[12];

message[0] = 0x80;
// いまから送信するコマンドに返信がいるかなどの設定
// 0x00 : ダイレクトコマンド送信。レスポンス必要。
// 0x01 : システムコマンド送信。レスポンス必要。
// 0x02 : 返信の送信。
// 0x80 : ダイレクトコマンド送信。レスポンス不要。
// 0x81 : システムコマンド送信。レスポンス必要。

message[1] = (byte)NxtCommand.SetOutputState;
// 送信するコマンド
// PlaySoundFile == 0x02 : サウンドファイルの再生
// SetOutputState == 0x04 : 出力用コマンド
// などなど。

message[2] = (byte)port;
// 出力ポートとして、x00-0x02を選択。
// 0xFFを指定すると全ポートに対する同一コマンド出力。

message[3] = (byte)power;
// 出力パワー。-100〜で指定する。負数だとモーターは逆回転する。
// 大きいほど勢いよく回転する。を指定すると他のパラメータがどうであれ停止する。

message[4] = (byte)mode;
// モーターの制御方法をビットフィールドで指定する。
// None == 0x00 : 何もしない。
// リセットしたり停止させたりするときに使う。(こともできる)
// ※ 常にMoterOnを指定してpower==0を指定して停止させるほうが制御が楽なように思う。
// MoterOn == 0x01 : モーターを回転させる。
// Brake == 0x02 : 指定したぶんだけモーターが回転したあとブレーキをかける。
// これを指定しないとモーターは慣性で動き続ける。
// ブレーキをかけたとしても、すぐに止まるわけではなく少しオーバーして回転する。
// Regulated == 0x04 : モーターのレギュレーションモードを用いる。
// これを指定した場合、次のバイトで指定するレギュレーションモードが有効になる。
// 負荷にかかわらず一定の速度でモーターを回したいときに用いる。
// (当然、このとき、MoterOnも指定していなければならないので、
// MoterOn | Brake | Regulated == 0x07を指定する)
// とは言っても、モーターの出力以上の回転がさせられるはずもなく、
// あまりに負荷が高いと指定した通りには回らない。

message[5] = (byte)regulationMode;
// レギュレーションモード
// ひとつ前のバイトでRegulatedを指定しなければ、ここの値は無視される。
// Idle == 0x00 : レギュレーションの指定をしない。
// ともかくモーターが指定したパワーで回ればいいだけならばこれで良い。
// MotorSpeed == 0x01 : モーターのスピードを指定する。
// これを指定した場合、負荷によらずpowerで設定したスピードに基づいて回転する。
// MotorSynchronization == 0x02 :
// 複数のモーターの動きを同期させる。
// 2つのモーターをぴったりと同じだけ回転させたいときに使う。
// これを指定するときは、ポートへの出力が有効になっていなければならない。
// (2回、SetOutputStateコマンドを別のポートに対して送信する必要がある。)

message[6] = (byte)turnRatio;
// 2つのモーターを同期させて動かすときの、動かす比率。
// -100〜100。
// 二つ前のバイトで、MoterOn | Brake | Regulated (Brake必須!)を選択し、かつ、
// ひとつ前のバイトでMotorSynchronizationを選択したときのみ有効。
//
// 出力ポートはつあるから、そのうちのつとなると以下の組み合わせが存在することになる。
// Left Right
// PortA PortB
// PortB PortC
// PortA PortC
// 上記の表により、擬似的な、LeftとRightが決定する。
//
// このパラメーターは、どれだけLeft,Rightに減速させるかの比率である。
// 負数ならLeftを基準としてRightが減速。正数ならRightを基準としてLeftが減速。
// 例)
// 0 なら、同じ方向にpowerで指定した速度で回転。
// 50 なら、Rightがpowerで指定した速度で回転、Leftは停止。
// 100 なら、Rightがpowerで指定した速度で回転し、Leftは-powerで回転。
// -50 なら、Leftがpowerで指定した速度で回転、Rightは停止。
// -100 なら、Leftがpowerで指定した速度で回転し、Rightは-powerで回転。
//
// 注意)
// 回転させるとき、現在どれだけ回転角させたかというカウンタ
// に依存して制御されるので、そをリセットしておかないと
// 意図した方向とは異なる方向に回転することがある。(ようだ) ←詳細未調査。
//

message[7] = (byte)runState;
// モーターの動作状態。
// Idle == 0x00 : 出力されない。
// RampUp == 0x10 : 指定の値になるまで、徐々に加速させる。
// Running == 0x20 : 指定の値で、そのまま走行する。
// RampDown == 0x40 : 指定の値になるまで、徐々に減速させる。

Util.SetUInt32(message, 8, tachoLimit);
// 9バイト目からバイト目まではuintで回転のリミットを指定する。
// ここで指定した分だけ回転すれば、停止する。
// 停止するとは言ってもピッタリ停止するわけではない。
// また、を指定すれば回り続ける。
// 単位は、角度。で一周。
// どこを(角度を測るときの)起点とするかは、ResetMotorPositionでリセットをかけて
// 起点の設定をするべし。←詳細未調査。

sendMessage(message);
// Bluttooth経由でメッセージが送信されるとき、実際は、先頭バイトに、
// メッセージ自体の長さを意味する値がUShort/リトルエンディアンで
// 付与された上で送信される。
// このモーター制御コマンドの場合は、+ 12バイト= 14バイトが送信される。
}


この記事を書いたあとに


■ ruby-nxt の使い方 - モーターを回す(基本編)
http://bearmini.net/blog/View.aspx?bid=1&aid=133


が追加された。あまりにも丁寧でわかりやすい記事なので、私も心が砕けそうになった(´ω`)

*1:Bluetooth Developer Kit: http://mindstorms.lego.com/Overview/NXTreme.aspx

*2: Executable File and Bytecode Reference (LEGO MINDSTORMS NXT Executable File Specification.zip 1.16MB) : http://mindstorms.lego.com/Overview/NXTreme.aspx