|
|
|
このページの内容
実例として、1秒に1回の割合でAIBOのLEDを点滅させることを考えてみましょう。必要な命令は、モーションマネージャが更新を問い合わせるたびに、LEDの新しい状態を計算するモーションコマンドです。
LedMCは、モーションコマンドとLEDエンジンの派生クラスで、このような要求を実現してくれます。具体的には、LedMCのオブジェクト
しかし、問題があります。モーションコマンドの計算を含むアプリケーションはMainプロセスで動いていますが、モーションマネージャはモーションプロセスで動いています。それでは、どうすればモーションマネージャが利用できるようにモーションコマンドを計算すればよいのでしょうか?
解決策は、両方のプロセスが共有するメモリ領域にモーションコマンドを作るのです。このようにすると、モーションコマンドに両プロセスからアクセスできるので、それが動いている間も、他の動きをさせるために設定値を変更できます(下図)。
しかし、モーションマネージャがモーションコマンドを呼び出している最中にモーションコマンドを修正するのは危険です(下図)。
この問題を回避するために、Tekkotsuでは、MMAccessorと呼ばれる相互排除メカニズムを提供しています。MMAccessorは、Mainプロセスからモーションコマンドのメンバー関数を呼び出す必要があるとき、モーションマネージャを一時的に排除します(つまり、モーションマネージャからアクセスできなくします)。このような関数を呼びたいときは、いつでもMMAccessorを作成してモーションコマンドをロックします(ロックする=カギをかけて他から使えなくする)。MMAccessorを削除するとモーションコマンドのロックは解除されます(下図)。
もう1つ行う作業があります。モーションコマンドがモーションマネージャに渡されるとき、MC_IDと呼ばれる識別IDをモーションコマンドに割り当てます。モーションコマンドをロックするためには、MC_IDの値をMMAccessorのコンストラクターに渡します。また、モーションコマンドを削除するときは、モーションマネージャにMC_IDを伝えます。このようにMC_IDはいろいろな場所で使用します。通常、MC_IDはBehaviorインスタンス内のprotectedメンバーとして扱います。
まとめ課題
練習: LEDを点滅させるためのモーションコマンドを設定しよう AIBOのLEDを点滅させるには、LedMCを使用します。また、ボタンを押すことにより、LED点滅パターンを変化させるようにしましょう。
最初にモーションマネージャを扱うために、新しいファイルをいくつかインクルードします。
また、モーションコマンドのMC_IDを記憶するために、protectedデータメンバーを作成します。
それから、MyMotionLed() コンストラクターを初期化します。 下記のプログラム(4つに分かれています)を、次のファイル名で保存して下さい。ファイル名: MyMotionLed.h
保 存 先: マイドキュメント → usXX → projectフォルダプログラムで赤字の箇所が新しく追加・変更された部分です。
モーションマネージャにアクセスするためにグローバル変数
#ifndef INCLUDED_MyMotionLed_h_ #define INCLUDED_MyMotionLed_h_ #include "Behaviors/BehaviorBase.h" #include "Events/EventRouter.h" #include "Motion/LedMC.h" // LedMCのヘッダファイル #include "Motion/MMAccessor.h" // MMAccessorのヘッダファイル #include "Motion/MotionManager.h" // MotionManagerのヘッダファイル class MyMotionLed : public BehaviorBase { protected: MotionManager::MC_ID leds_id; // モーションコマンドに対するID public: MyMotionLed() : BehaviorBase("MyMotionLed"), // コンストラクターを初期化 leds_id(MotionManager::invalid_MC_ID) {} // コンストラクターを初期化motman
を使用します。
DoStart() は、SharedObject<LedMC>テンプレートクラスを使用してインスタンスleds_mcを作ります。
つまり、leds_mcはSharedObjectクラスのインスタンスで、引数の型がLedMCです。
SharedObjectのインスタンスは共有メモリ内に作成されます。
これで、leds_mcが共有オブジェクトになります。1000ミリ秒ごとにLedを光らせるために、
cycle(...)
メソッドを呼びます。
それから、addPersistentMotion
を使用して、leds_mcをモーションマネージャに追加します。
leds_mcはローカル変数なので、DoStart()が再び呼ばれると、失われます。
以上でモーションコマンドを設定できました。
leds_idはモーションマネージャのリストからleds_mcを指定するための識別IDになります。
モーションコマンドは、モーションマネージャのリストから取り除かれるまで消えません。DoStop() は、モーションマネージャのリストからLedMCを削除します。
virtual void DoStart() { BehaviorBase::DoStart(); std::cout << getName() << " is starting up." << endl; erouter->addListener(this,EventBase::buttonEGID); SharedObject<LedMC> leds_mc; leds_mc->cycle(RobotInfo::FaceLEDMask, 1000, 100.0); leds_id = motman->addPersistentMotion(leds_mc); }processEvent() では、LedMCのパラメータを変更します。パラメータとは設定値のことです。
virtual void DoStop() { motman->removeMotion(leds_id); std::cout << getName() << " is shutting down." << endl; BehaviorBase::DoStop(); }
パラメータ変更を安全に行うために、MMAccessorのインスタンスleds_accを作ります。
このとき、識別IDのleds_idを使用します。これでモーションコマンドがロックされます。
それから、cycle()関数を呼び、点滅周期を変化させます。
最初の式1000-750*event.getMagnitude()
において、getMagnitude()は、0〜1.0の間の値になります。
ボタンを押したときが1.0で、離したときが0です。
だから式の結果として、点滅周期はボタンを押している時が250ミリ秒で、押していない時が1000ミリ秒です。
関数の最後の引数100.0は明るくしたり暗くしたりする速度を決める値です。
100.0の場合、すぐに明るくなります
cycle()関数は正弦波を計算します。
正弦波の振幅は0〜1.0の間です。
したがって、正弦波に100.0を掛け算することは、波形の正の部分がすぐに1.0になるような階段波形を作り出します。
virtual void processEvent(const EventBase &event) { switch ( event.getGeneratorID() ) { case EventBase::buttonEGID: { std::cout << getName() << " got event: " << event.getDescription() << std::endl; MMAccessor<LedMC> leds_acc(leds_id); leds_acc->cycle(RobotInfo::FaceLEDMask, int(1000-750*event.getMagnitude()), 100.0); break; }; default: std::cout << "Unexpected event:" << event.getDescription() << std::endl; } } }; #endif
LEDの種類や色を変更するには、次の赤字の箇所を変更します。
leds_mc->cycle(RobotInfo::FaceLEDMask, 1000, 100.0);
次の表を参考にして、いくつかのLEDを光らせて試しなさい。
表.LEDの記号 記号 場所 色 HeadColorLEDMask 頭の半円 オレンジ HeadWhiteLEDMask 頭の半円 ホワイト WirelessLEDMask 頭の頂上 ブルー ModeRedLEDMask 耳の横 レッド ModeGreenLEDMask 耳の横 グリーン ModeBlueLEDMask 耳の横 ブルー FaceLEDMask 顔 ホワイト&レッド AllLEDMask すべて BackLEDMask 背中の全て FrBackColorLEDMask 背中の前側 ブルー FrBackWhiteLEDMask 背中の前側 ホワイト MdBackColorLEDMask 背中の中央 オレンジ MdBackWhiteLEDMask 背中の中央 ホワイト RrBackColorLEDMask 背中の後側 レッド RrBackWhiteLEDMask 背中の後側 ホワイト
cycle
で設定すると動き続けます。しかし、パラメータを flash
または cflash
で設定すると、自動的に終了します。これらはLEDを一度だけ光らせるのです)。モーションコマンドは、そのモーションが終了するとイベントを発生させて終了したことを通知します。イベントには source IDが含まれます。この source ID はモーションコマンドのMC_IDと一致します。
RobotInfo::MaxOutputSpeed[]
に保存されています。
各関節が動く範囲にはソフトウェアでの限界とハードウェアでの限界があります。
ソフトウェア限界は配列 RobotInfo::outputRanges[][]
に保存されています。これは上述の head motion の例で使用しました。
ハードウェア限界は配列 RobotInfo::mechanicalLimits[][]
に保存されています。
すべてのテーブルは同じ index を使用します。例えば、頭を上下に動かすモーターのindex はどのテーブルでも HeadOffset+TiltOffset です。
Tekkotsu で関節を参照する際の約束として、これらのテーブルで offset を指定します(offsetというのは基準からの距離です)。これらの offset は整数ではなく HeadOffset のように記号で表現します。
RobotInfo::LegOrder_t
で定義され、次の通りです。それぞれの足には3個の関節があり、
値 記号 意味 0 LFrLegOrder Left Front Leg (左前脚) 1 RFrLegOrder Right Front Leg (右前脚) 2 LBkLegOrder Left Back Leg (左後脚) 3 RBkLegOrder Right Back Leg (右後脚)
RobotInfo::NumLegJoints
の値が3に定義されています。
LFrLegOffset == LegOffset + LFrLegOrder * NumLegJoints
RFrLegOffset == LegOffset + RFrLegOrder * NumLegJoints
LBkLegOffset == LegOffset + LBkLegOrder * NumLegJoints
RBkLegOffset == LegOffset + RBkLegOrder * NumLegJoints
1本の足における関節の順番は RobotInfo::REKOffset_t
で定義されています。それぞれの関節の値は 0,1,2で、記号では、RotatorOffset
, ElevatorOffset
, KneeOffset
です。Rotator と Elevator は共に股関節ですが、Rotatorは足を前後に動かす関節、Elevatorは足を開いたり閉じたりする関節です。Knee は膝関節です。
特定の足の関節を参照するには、その足の offset を使用し、さらに関節の offset を追加します。例えば、右の後ろ足の膝関節は RBkLegOffset + KneeOffset
で参照します。
同様な決まりが頭部にも適用されます。RobotInfo::HeadOffset
です。RobotInfo::TPROffset_t
で定義されます。TPRというのは Tilt, Pan, and Roll のことです(ただし、ERS-7 はRollを持たず、代わりに第2のTiltとして Nodを持ちます)。TPROffset の値は、それぞれ TiltOffset
, PanOffset
, RollOffset
(ERS-7の場合は NodOffset
)。例えば、頭部Tilt関節に関する情報にアクセスするには、indexとしてHeadOffset+TiltOffset
を使用します。
関節は常にすべての index により参照されます。ただ、1つ例外があり、HeadPointerMC モーションコマンドは、HeadOffset を使用せずにTPROffsetで参照します。例えば、HeadPointerMC を用いて頭部Panの角度を設定したいときは次のように書きます。
head_acc->setJointValue(PanOffset, panvalue)
しかし、ロボット全体の姿勢としてPanの角度を調整したいときは、HeadPointerMC でなく PostureMC を使用するので、次のように書きます。
posture_acc->setOutputCmd(HeadOffset+PanOffset, panvalue)
PostureMCについては別のページで勉強します。
|
|
|