
図:2リンク機構に初期角速度を与えるシミュレーション
CohCohさんのご質問で初期の関節角速度の設定法がありましたので、久しぶりにODE(Open Dynamics Engine)講座を開講します。
ODEでは関節にモータが標準で付属していますが、関節角速度を設定しようとしても、目標角速度に到達するまで時間がかかりますし、その角速度を維持するためにはそれなりの制御が必要です。現実のモータを単純にモデル化したものです。
これでは、初期角速度を設定できません。この問題を解決する方法として、ODEには理想的なAMOTORがあります。マニュアルでは1ステップで目標角速度に到達するとありますが、関節に与える最大トルクdParamFmaxの値が0だと目標角速度に到達しません。また、小さな値の場合は、目標角速度に到達するまで数ステップかかることを確認しました。
そこで、十分大きな値を設定してください。以下のサンプルプログラムでは目標角速度dParamVelに無限大dInifinityを与えています。
また、初期角速度だけ与えたい場合は、角速度が目標値に到達したら、dJointDestroy()でamotorを破壊する必要があります。これを忘れると常に角速度は目標角速度のままです。amotorを破壊したくない場合は、dJointSetAMotorNumAxes(amotor, 0)で制御する軸数を0に設定し、無効化してもOKです。
次に、APIを紹介します。
dJointID dJointCreateAMotor(dWorldID, dJointGroupID);
AMOTORの生成。void dJointSetAMotorMode(dJointID, int mode);
AMOTORのモード設定。modeはユーザが独自に設定するdAMotorUserまたは、自動的にオイラー角が計算されるdAMotorEulerの2つのモードがある。void dJointSetAMotorNumAxes(dJointID, int num);
AMOTORでコントロールされる軸数を設定する。numの値は0から3までで、0の場合はジョイントを無効化する。dAMotorEulerモードの場合は自動的に3が設定される。void dJointSetAMotorAxis (dJointID, int anum, int rel, dReal x, dReal y, dReal z);
AMOTORの回転軸を設定する。引数anumは変更する軸(0, 1, 2)、relは以下の相対回転モードを設定する。最後の(x,y,z)は回転軸ベクトル。
- 0: 軸は絶対座標系に固定
- 1: 1番目のボディに固定
- 2: 2番目のボディに固定
void dJointSetAMotorParam (dJointID, int parameter, dReal value);
AMOTORのパラメータを設定する。 parameterについては他のジョイントと同じ。この例では角速度(スライダージョイントの場合は速度)dParamVel、最大発揮トルクdParamFMaxを使用した。
AMOTORはAngular Motorの略ですが、エー(良い)モータですね。ソースコードを以下に示しますので試してください。
なお、簡単にするために衝突検出機能は実装されていません。ここからダウンロード可能です(amotor2-080115.tgz)。
-
// 2008-1-15
-
// amotor2.c: 2 Links with an amotor by Kosei Demura
-
#include
-
#include
-
#include
-
#include
-
-
#define NUM 2
-
#ifdef dDOUBLE
-
#define dsDrawCylinder dsDrawCylinderD
-
#endif
-
-
dWorldID world;
-
dSpaceID space;
-
dGeomID ground;
-
dJointID joint[NUM], amotor;
-
dsFunctions fn;
-
-
typedef struct {
-
dBodyID body;
-
dGeomID geom;
-
} MyLink;
-
-
MyLink rlink[NUM];
-
-
void createArm()
-
{
-
dReal init_avel = 1.0; // 初期角速度[rad/s]
-
dMass mass;
-
dReal x[] = { 0.00, 0.00}, y[] = { 0.00, 0.00}, z[]= { 0.75 ,0.25}; // 重心
-
dReal length[] = { 0.50, 0.50}; // リンク長
-
dReal weight[] = { 1.00, 1.00}; // 質量
-
dReal r[] = { 0.02, 0.02}; // 半径
-
dReal anchor_x[] = { 0.00, 0.00}; // 回転軸中心
-
dReal anchor_y[] = { 0.00, 0.00};
-
dReal anchor_z[] = { 1.00, 0.50};
-
dReal axis_x[] = { 1.00, 1.00}; // 回転軸ベクトル
-
dReal axis_y[] = { 0.00, 0.00};
-
dReal axis_z[] = { 0.00, 0.00};
-
-
// リンクの生成
-
for (int i = 0; i <NUM; i++) {
-
rlink[i].body = dBodyCreate(world);
-
dBodySetPosition(rlink[i].body, x[i], y[i], z[i]);
-
dMassSetZero(&mass);
-
dMassSetCylinderTotal(&mass,weight[i],3,r[i],length[i]);
-
dBodySetMass(rlink[i].body, &mass);
-
rlink[i].geom = dCreateCylinder(space,r[i],length[i]);
-
dGeomSetBody(rlink[i].geom,rlink[i].body);
-
}
-
-
//ジョイントの生成と結合
-
for (int j = 0; j <NUM; j++) {
-
joint[j] = dJointCreateHinge(world, 0);
-
-
// リンク1はjoint[0]により静的環境0と結合
-
if (j == 0) dJointAttach(joint[0], rlink[0].body, 0);
-
else dJointAttach(joint[j], rlink[j].body, rlink[j-1].body);
-
dJointSetHingeAnchor(joint[j], anchor_x[j], anchor_y[j], anchor_z[j]);
-
dJointSetHingeAxis(joint[j], axis_x[j], axis_y[j],axis_z[j]);
-
}
-
-
// リンク1とリンク2を結合しているジョイントを可動域設定
-
dJointSetHingeParam(joint[1], dParamLoStop, -0.0 * M_PI); // 可動域の下限
-
dJointSetHingeParam(joint[1], dParamHiStop, 0.0 * M_PI); // 可動域の上限
-
-
// amotorの生成
-
amotor = dJointCreateAMotor(world, 0); // AMotorの生成
-
dJointAttach(amotor, rlink[0].body, 0); // AMotorとリンクとの結合
-
dJointSetAMotorMode(amotor, dAMotorUser); // ユーザモードに設定、デフォルト、省略可
-
dJointSetAMotorNumAxes(amotor, 1); // AMotorでコントロールする軸数
-
-
// 軸の設定:回転軸0を絶対座標系に固定、回転軸ベクトルは(1, 0, 0)
-
dJointSetAMotorAxis(amotor, 0 /*軸0*/, 0 /*絶対座標系に固定*/, 1 /*x成分*/, 0 /*y成分*/, 0 /*z成分*/);
-
-
// amotorの設定, 初期角速度をinit_avelに設定
-
dReal fmax = dInfinity; // 無限大
-
dJointSetAMotorParam(amotor, dParamVel, init_avel);
-
dJointSetAMotorParam(amotor, dParamFMax, fmax);
-
}
-
-
void drawLink()
-
{
-
dReal r,length;
-
-
for (int i = 0; i <NUM; i++ ) {
-
if (i == 1) dsSetColor(1.3, 1.3, 0.0);
-
else dsSetColor(0.0, 0.0, 1.3);
-
dGeomCylinderGetParams(rlink[i].geom, &r,&length);
-
dsDrawCylinder(dBodyGetPosition(rlink[i].body),
-
dBodyGetRotation(rlink[i].body),length,r);
-
}
-
}
-
-
void start()
-
{
-
float xyz[3] = { 2, 0, 1};
-
float hpr[3] = {-180, 0, 0.0};
-
dsSetViewpoint(xyz,hpr);
-
}
-
-
// 簡単にするため衝突検出は未実装
-
void simLoop(int pause)
-
{
-
static int step = 0;
-
-
drawLink();
-
const dReal ang_rate = dJointGetHingeAngleRate(joint[0]);
-
printf("step=%4d: angle rate=%8.6f \n", step, ang_rate);
-
dWorldStep(world, 0.001);
-
-
dReal k1 = 0.0; // 摩擦トルク用比例係数 負値を設定
-
dJointAddHingeTorque(joint[0], k1 * ang_rate); // 摩擦トルクを付加
-
-
if (step == 0) dJointDestroy(amotor); // amotorの破壊
-
// amotorを破壊したくない場合は以下でも良い
-
// if (step == 0) dJointSetAMotorNumAxes(amotor, 0);
-
-
step++;
-
}
-
-
void setDrawStuff() {
-
fn.version = DS_VERSION;
-
fn.start = &start;
-
fn.step = &simLoop;
-
fn.path_to_textures = "../../drawstuff/textures";
-
}
-
-
int main(int argc, char *argv[])
-
{
-
setDrawStuff();
-
world = dWorldCreate();
-
space = dHashSpaceCreate(0);
-
// dWorldSetGravity (world, 0, 0, -9.8); // 重力加速度
-
createArm();
-
dsSimulationLoop(argc,argv,640, 480,&fn);
-
dSpaceDestroy(space);
-
dWorldDestroy(world);
-
return 0;
-
}
終わり。

