シーングラフのチュートリアルもそろそろリニューアルが必要なので,こちらに書き直します. 古いものは http://springhead.info/src/html/ja/TutorialVC.html にあります.
まずは,慣れるために既存のプロジェクト(今回は Springhead/src/Samples/DynaHaptic)を実行してみます.
↑:前進 ↓:後退 ←:左回転 →:右回転 S:上回転 X:下回転 Q:左平行移動 W:右平行移動 A:上平行移動 Z:下平行移動 R:視点リセット F:フルスクリーン G:画面をフルスクリーンから元に戻す C:SPIDARのグリップのキャリブレーション スペース:SPIDARに力を返すか返さないかのON-OFF M:マルチメディアタイマーの起動 (精度の高い力覚を提示するためには必要.Release版のみ)
ボールを転がして箱に入れるという仮想世界の作成を通して, Xファイルの編集の方法を簡単に見ていきます
まず,仮想世界に必要なオブジェクトとして, 床,ジャンプ台となる三角柱,ボールをモデリングします. .x形式で保存することのできるモデリングソフトを使用して作ってください.
−作成例−
floor.x
trianglePole.x
sphere.x
仮想世界に登場する物体にはさきほど作成したような直方体,球のような凸物体と, 箱のような凹物体があります. Springheadでは衝突判定に用いているアルゴリズムの性質上, 物体はすべて凸物体でなければなりません. そこで,箱を作るときには,底と四方の壁の5つの直方体を組み合わせて作成します. 作成例では,親フレーム Vessel の下に 子フレームとして VesselFront,Back,Left,Right,Bottom を配置しています. 各フレームにある FrameTransformMatrix は アフィン行列を転置した形で記述されており, 親フレームからのローカル変換を表しています. (VesselFront,Back,left,right はこのローカル変換が異なるだけで, その下に記述されている Mesh の部分は同じものを使用しています)
−作成例−
では実際に世界を作っていきます. まず,世界の一番の親フレームとして frTop を作成し, その子フレームに床を,そして床の子フレームとして三角柱を配置します.
−作成例−
次は,床を傾けます. さきほど,三角柱を床の子フレームとしているので, 床フレームの FrameTransformMatrix を編集するだけで, 三角柱ごと傾けることができます.
−作成例−
ボールを frTop の子フレームとして配置します.
−作成例−
ここまでの状態で実行しても, 暗くてあまり見えないのでライトを追加します. 具体的には,ライトの位置・姿勢を決めるためのフレームと そのライトの性質を示す Light8 を frTop の下に追加します.
−作成例−
ではいよいよ仮想世界に物理法則を追加します. 具体的には,
−作成例−
物体に摩擦力などのパラメータを追加したい場合、対象のフレームのMeshの下(Meshの{}内)に PhysicalMaterialを追加する。
Mesh{ ・・・・・ PhysicalMaterial{ 1.0f; #抗力のばね係数 1.0f; #抗力のダンパ係数 1.0f; #摩擦力のばね係数 1.0f; #摩擦力のダンパ係数 0.6f; #最大静止摩擦係数 0.3f; #動摩擦係数 } ・・・・・ }
このままでは画面に映る範囲が狭いので, 少しひいた(ズームアウトした)位置に視点を動かします. 視点の位置を設定するためには,frTop の下に Camera を追加します. Camera がない場合はデフォルトのカメラ位置になります. (デフォルトのカメラ設定については http://springhead.info/src/src/Graphics/GRCamera.cpp の GRCameraData::InitData() を参照) これまではカメラの設定をしていなかったので, デフォルトのカメラ位置になっていたということになります. カメラの位置の設定には,保存の機能を使うと便利です. 保存を行うと,そのときのカメラの設定をXファイルに書き出してくれます. よって,
実行 → キー操作により任意の視点位置に移動 → “名前を変えて”保存 → 保存したXファイルのカメラ設定を元のファイルにコピー
という手順で行うと楽です. 保存する際,ボールの位置など仮想世界の状態も一緒に保存してしまうので, 別の名前で保存する必要があります.
−作成例−
次は,床を広くして,ボールももっと上から転がるように変更します. 床を広くするためには物体の頂点座標を管理している Mesh を変更します. ボールの位置の変更は FrameTransformMatrix の平行移動成分を変更して行います.
−作成例−
ボールをキャッチする箱を配置します. 凹物体を作る で作成した箱フレームを追加し, ボールとの衝突判定を行うように Penalty も追加します.
−作成例−
箱にボールが入らなかったので,ボールがもっと遠くへ飛んでいくように, Solid を編集して初期速度を大きくします.
−作成例−
箱にボールが入るところまでできたので,少し見た目を向上させるために, 背景を付け,テクスチャも貼ります. 背景は,世界の後方に大きな平面オブジェクトを置いて作ることにします. テクスチャは,Material の中の TextureFilename で指定します.
−作成例−
つづいて rollingBall を改良して golf を作っていきます. まずは,世界をgolf用に変更します.
−作成例−
SPIDARのグリップ部分に対応するポインタを作ります. まずは,ポインタのフレームを作成します. そして,ポインタの物理(Solid,Penalty)を追加します. データファイル側の作業は以上で終わりです.
−作成例−
もうしばらくおまちください....
自分用に新しくプロジェクトを作ります. プログラムに変更を加えていく場合は, 新しいプロジェクトを作ることをお勧めします. 今回は,簡単な方法ということで既存のプロジェクト(Springhead/src/Samples/DynaHaptic)を コピーして名前だけ変更するという方法を取ることにします.
Springheadは関節を持つ物体をFeatherstoneの方法を使ってシミュレーションします.
関節を作るには,次のようにします.
JointEngine { { soBody } // ルートの剛体,固定する場合は,Frameにする. JointHinge joUpper { // 蝶番関節の定義.スライダーの場合は,JointSlider 1.000000,0.000000,0.000000, 0.000000,1.000000,0.000000, 0.000000,0.000000,1.000000;; // 親剛体のフレームから見たジョイントの向き 0.800000;0.400000;0.000000;; // 親剛体のフレームから見たジョイントの位置 1.000000,0.000000,0.000000, 0.000000,1.000000,0.000000, 0.000000,0.000000,1.000000;; // 子剛体のフレームから見たジョイントの向き -0.500000;0.000000;0.000000;;; // 子剛体のフレームから見たジョイントの位置 0.000000;0.000000;-0.143350;-0.715527;; // 位置・速度・可動範囲(最小,最大) { soUpper } // joUpperの子剛体,joSideの親剛体 JointHinge joSide { 0.000000;0.000000;0.000000;; 0.000000,0.000000,-1.000000, 0.000000,1.000000,0.000000, 1.000000,0.000000,0.000000;; 0.000000;0.000000;0.000000;; 0.000000,0.000000,-1.000000, 0.000000,1.000000,0.000000, 1.000000,0.000000,0.000000;;; 0.000000;0.000000;0.106441;-0.336312;; { soSide } JointHinge joLower { 1.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,1.000000;; 0.000000;-0.400000;-0.200000;; 1.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,1.000000;; 0.000000;0.400000;0.000000;;; 0.000000;0.000000;-0.613165;-2.209808;; { soLower } } } } //...(以下,右前足,左後ろ足,右後ろ足の記述が続きます.) } JointPid pidUpper { // joUpper用のPIDコントローラ 0; // 軸の番号いまのところ0〜3.ボールジョイントはたぶんうまく制御できない. 0; // 制御対象:0:位置, 1:速度 1000.000000; // 比例制御係数:PIDのP 0.000000; // 積分制御係数:PIDのI 20.000000; // 微分制御係数:PIDのD 0.276041; // 制御目標位置 { joUpper } // 対象関節 } JointPid pidSide { 0;0; 1000.000000; 0.000000; 20.000000; 0.100000; { joSide } } JointPid pidLower { 0;0; 150.000000; 0.000000; 5.000000; -0.544807; { joLower } }
関節エンジンPHJointEngine はひとつのリンク機構(Articulated Body)をあらわします.2つ以上の機構を作りたい場合は,PHJointEngineを2つ以上書いてください.
関節エンジンPHJointEngineの中には,ひとつの剛体 or フレームとひとつの関節ノードを書きます. 関節ノードの中には,ひとつの剛体と複数の関節ノードをかけます.
以下関節ノードを列挙,説明します.
1自由度 | PHJointHinge, PHJointSlide |
2自由度 | PHJointUniversal |
3自由度 | PHJointBall |
関節軸を制御するノードとして,PHJointPidとPHJointBallPidがある.PHJointPidは関節の軸1つに対してPID制御をかけます.そのため,ボールジョイントの制御には使えません. PHJointBallPidは,ボールジョイントが目標姿勢に向かいように制御をかけます.
JointPid { 0; #軸の番号 JointUnivの場合,0と1両方に制御をかけなければならない. 0; #0:位置制御, 1:速度制御 10.0; // PID の P 比例係数 1.0; // PID の I 積分係数 1000.0; // PID の D 微分係数 1.57; // PIDの目標値 {jnt01} } JointBallPid { 100.0; 0.0; 100.0; #PID 0.0; 1.0; 0.0; 0.0;; #目標姿勢(Quaternion w x y z) {jnt01} }
関節を動かすのは,C++のプログラムで行うほうが便利ですが,ちょっと試すために周期的に動かす ためのエンジンを用意しました.
JointControlEngine{ 1.0; 1.0; # 周期(s), 速度の倍率 JointControl{ 0.0; 35.0; 60.0; 1.0; # 始点(deg),終点(deg),位相(deg), 速度の倍率 {joUpperPid} # 適用するJointPidノードの名前 } JointControl{ -10.0; -40.0; 90.0; 1.0; {joLowerPid} } }
これはジョイントの目標角度を変化させますので,Jointの入力を位置にしておく必要があります
改良を続けているため,フォーマットが複数あります. 古いXファイルは次のようになっているかもしれません. 一度ロードしてセーブすると新しい書き方に書き換わります.
JointEngine{ {soBody} Joint joUpper{ 0; #関節の種類 0:蝶番, 1:スライド 0.8; 0.0; 0.6;; #親剛体から見た関節の位置 1.0, 0.0, 0.0, #親剛体から見た関節の向き(3x3行列) 0.0, 1.0, 0.0, 0.0, 0.0, 1.0;; 0.0; 0.4; 0.0;; #子剛体から見た関節の位置 1.0, 0.0, 0.0, #子剛体から見た関節の向き(3x3行列) 0.0, 1.0, 0.0, 0.0, 0.0, 1.0;; #変位, 速度, 最大トルク, 可動範囲-, 可動範囲+ (可動範囲は未実装) 0.0; 0.0; 20000000.0; 0.0; 0.0; #入力の種類(0:トルク, 1:加速度, 2:速度, 3:変位), 入力値,P,I,D 3; 0.1; 1000.0; 0.0; 20.0; #位置入力の例 # 2; 2.0; 100.0; 0.01; 0.5; #速度入力の例 # 3; 0.0; 0.0; 0.0; 0.0; {soSide} Joint joSide{ #左前足 0; 0.0; 0.0; 0.0;; 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0;; 0.0; 0.0; 0.0;; 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0;; 0.0; 0.0; 20000000.0; 0.0; 0.0; 3; 0.1; 1000.0; 0.0; 20.0; {soUpper} Joint joLower{ 0; 0.0; -0.4; 0.2;; 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0;; 0.0; 0.4; 0.0;; 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0;; 0.0; 0.0; 20000000.0; 0.0; 0.0; 3; 0.0; 150.0; 0.0; 5.0; {soLower} } } } 以下,右前足,左後ろ足,右後ろ足の記述が続きます.
人体のモデルを作る場合,2,3重関節が必要になります.
%%現状では,Solidの指定なしの単関節を組み合わせて実現しています. 本来は質量0でなければなりませんが,現在のJointEngineでは質量0のリンクを作ると計算できなくなります.そのため,Jointの中に{Solidの名前}がない場合,適当な質量のSolidが自動的に割り当てられます.%%
将来的には,Jointの種類として,2重,3重関節を用意する予定です.
用意しました.
ジョイントのバネダンパ定数は注意深く指定しないと発振などの問題が起こり, シミュレータを不正終了させてしまいます. その関節が支えている部分の質量を基準にして,バネダンパ定数を設定すると良いようです.
現在,佐藤研でも人体モデルとその関節の制御をするプログラムを開発中です. (できるだけ速やかに公開したいと考えています.)