ODE(Open Dynamics Engine)初級講座の7回目です。今回は,ボディ(剛体,body)を他のボディとくっつけるジョイント(関節)について説明します。
ジョイントは我々の周りでは、折畳み携帯のヒンジやドアの蝶番に相当します。小難しくいうと、2つのボディの位置や姿勢をある一定の関係に保つ拘束がジョイントとなのです。ODEではジョイントと拘束を同じ意味で使っています。ジョイントをうまく使うと物体の運動を直線上に拘束させたり、円軌道に拘束させたりすることもできます。
- ジョイントの使い方
- ***ジョイントの生成 dJointCreate***()
- ***ジョイントとボディの結合 dJointAttach()
- ***ジョイントの中心点を設定 dJointSet***Anchor()
- ***ジョイントの回転軸を設定 dJointSet***Axis()
上で***にはジョイントのタイプが入ります。タイプにはHinge(ヒンジ)、Ball(ボール)、Slider(スライダー)、Universal(ユニバーサル)等があります。詳しい使い方はManual(英語)やODE本を参照してください。
- ジョイントのパラメータ設定
- 可動域の設定
dJointSetHingeParam(dJointID, dParamLoStop, 可動域の下限);
dJointSetHingeParam(dJointID, dParamHiStop, 可動域の上限); - 目標角速度とそれを実現するための最大トルクの設定
dJointSetHingeParam(dJointID, dParamVel, 目標角速度);
dJointSetHingeParam(dJointID, dParamFMax, 最大トルク);
パラメータとしては、関節可動域の下限を示すdParamLoStop、上限を示すdParamHiStop、角速度(ヒンジジョイント)または速度(直動式関節)を示すdParamVel、最大トルクを示すdParaFMaxなどがあります。なお、ODEでは関節にモータが標準で組み込まれているので、dParamVelやdParaFMaxを指定すると関節が動きます。
- サンプルプログラム
さっそく上図に示している玉と円柱をヒンジジョイントで結合しているオブジェクトがバウンドするソースコードを以下に示します。なお、前回までと同じ関数や宣言は省略しています。
// sample7.cpp by でむ 2006-2008 #ifdef dDOUBLE #define dsDrawSphere dsDrawSphereD // 単精度と倍精度の描画関数に対応するおまじない #define dsDrawCapsule dsDrawCapsuleD #endif typedef struct { dBodyID body; // ボディ(剛体) dGeomID geom; // ジオメトリ(形状) dReal radius, length, mass; // 半径[m]、 長さ[m]、 質量[kg] } myLink; myLink ball, pole; // simLoopはこのソースではわからないがwhileループに入っていて、シミュレーションの各ステップで実行される。 static void simLoop (int pause) { const dReal *pos1,*R1,*pos2,*R2; // 配列へのポインタが代入される dSpaceCollide(space,0,&nearCallback); // 衝突検出計算 dWorldStep(world,0.01); // 動力学計算を1ステップ(ここでは0.01秒)進める dJointGroupEmpty(contactgroup); // 接触点グループの後始末 dsSetColor(1.0,0.0,0.0); // 赤色の設定 pos1 = dBodyGetPosition(ball.body); // pos1(位置)は配列のポインター R1 = dBodyGetRotation(ball.body); // 回転行列の取得 dsDrawSphere(pos1,R1,ball.radius); // 球の描画 // 円柱の描画 pos2 = dBodyGetPosition(pole.body); // 位置の取得 R2 = dBodyGetRotation(pole.body); // 姿勢の取得 dsDrawCapsule(pos2,R2,pole.length,pole.radius); // カプセルの描画 } // オブジェクトの作成 void createBallPole() { dMass m1; dReal x0 = 0.0, y0 = 0.0, z0 = 2.5; //玉 ball.radius = 0.2; ball.mass = 1.0; ball.body = dBodyCreate(world); dMassSetZero(&m1); dMassSetSphereTotal(&m1,ball.mass,ball.radius); dBodySetMass(ball.body,&m1); dBodySetPosition(ball.body, x0, y0, z0); ball.geom = dCreateSphere(space,ball.radius); dGeomSetBody(ball.geom,ball.body); // カプセル pole.radius = 0.025; pole.length = 1.0; pole.mass = 1.0; pole.body = dBodyCreate(world); dMassSetZero(&m1); dMassSetCapsuleTotal(&m1,pole.mass,3,pole.radius,pole.length); dBodySetMass(pole.body,&m1); dBodySetPosition(pole.body, x0, y0, z0 - 0.5 * pole.length); pole.geom = dCreateCapsule(space,pole.radius,pole.length); dGeomSetBody(pole.geom,pole.body); // ヒンジジョイント joint = dJointCreateHinge(world, 0); // ヒンジジョイントの生成 dJointAttach(joint, ball.body,pole.body); // 玉と円柱のボディをジョイントで結合 dJointSetHingeAnchor(joint, x0, y0, z0 - ball.radius); // ヒンジのアンカー(中心点)を設定 dJointSetHingeAxis(joint, 1, 0, 0); // ヒンジの回転軸ベクトルを設定(1,0,0) } int main (int argc, char **argv) { setDrawStuff(); // 描画関数の設定 dInitODE(); // ODEの初期化 world = dWorldCreate(); // 動力学ワールドの生成 space = dHashSpaceCreate(0); // 衝突計算スペースの生成 contactgroup = dJointGroupCreate(0); // 接触点グループの生成 dWorldSetGravity(world,0,0,-9.8); // 重力加速度の設定 ground = dCreatePlane(space,0,0,1,0); // 地面用平面ジオメトリの生成 createBallPole(); // 玉とカプセルの生成 dsSimulationLoop (argc,argv,352,288,&fn); // シミュレーションループ dWorldDestroy (world); // ワールドの破壊 dCloseODE(); // ODEの終了 return 0; }
この例では玉と円柱のボディをヒンジジョイントで結びつけています。
ではソースコードを読みましょう。createBallandPall関数ではまず玉を作り、その後、円柱を作り、それからヒンジジョイントを dJointCreateHinge()で 生成し、dJointAttach()で2つのボディをヒンジジョイントで結合します。その後のdSetHingeAnchor()はジョイントの回転中 心点の位置を設定し、dJointSetHingeAxis()で回転軸ベクトル を設定します。この例では回転軸ベクトルは(1,0,0)なのでx軸方向となります。詳しくはマニュアルを参照。
これでジョイントの生成は終わりです。sample7は簡単のためにジョイントのパラメータを設定しませんでしたが、可動域や最大トルク、最大角速度の設定なども可能です。次からサンプルプログラムをダウンロードして遊んでください。
なお、説明でわかりずらい箇所や質問があればコメントを投稿してください。
今回登場したAPI
- void dJointAttach(dJointID joint, dBodyID body1, dBodyID dBody2)
剛体body1とbody2にジョイントjointを取り付ける
- dJointID dJointCreateHinge(dWorldID world, dJointGroupID)
worldにヒンジジョイントを生成し、そのID番号を返す。dJointGroupIDには0を入れる。
- void dJointSetHingeAnchor(dJointID joint, dReal x, dReal y, dReal z)
ヒンジジョイントの回転軸の中心点(x,y,z)を設定する。
- void dJointSetHingeAxis(dJointID joint, dReal x, dReal y, dReal z)
ヒンジジョイントの回転軸ベクトル(x,y,z)を設定する。