2015年11月15日日曜日

コマ こま ボールジョイント ジャイロ効果

コマと緑の棒をボールジョイント、
緑の棒と青の棒はヒンジジョイント(コントロールして90度に固定)
してみた。
ジャイロ効果によりコマの回転軸がまわる。
コマ回転方向はz軸右ねじ回転、重力はもちろん下、でコマの軸は
重力と90度方向の力を受ける。

//http://www.natural-science.or.jp/article/20110216235836.php
//Open Dynamics Engine 入門【5日目】デモ「こま」
//上のコードから片方のコマ(コマ1)だけポインタを使わずにコードをかく
//コマを ひとつにする
//
//コマはどんな関数で回るのか? ジョイントがあって回転させる関数はわかるが。
//以下のコマンドでこまは回る
// → dBodySetAngularVel( koma_body1, 0,0,5);
//
//元コードのポインタ表現のコードをコメントアウトして残しておく
//構造体を使ってみる。そしてコードを少し改変。
//
//なんで、丁度軸の下が地面に接触するように設定されているのに、実行したとき
//こまが落ちてくるように描画されるのか? 以下に設定していて勘違いをしているので
//は?
//    dBodySetPosition(koma[i].body,0.8f, -2, 2) → -2, 2, 0.8f
//    修正する。
//
//    コマの下の軸に.ボールジョイントをつけてそれを空中に固定する
//
//    ジャイロ効果を見るため、最初からコマを傾かせておく

#include <ode/ode.h>
#include <drawstuff/drawstuff.h>
//#include "texturepath.h"

#ifdef _MSC_VER
#pragma warning(disable:4244 4305)  // for VC++, no precision loss complaints
#endif

#ifndef DRAWSTUFF_TEXTURE_PATH
#define DRAWSTUFF_TEXTURE_PATH "C:/ode-0.11.1/drawstuff/textures"
#endif
// select correct drawing functions

#ifdef dDOUBLE
#define dsDrawBox dsDrawBoxD
#define dsDrawSphere dsDrawSphereD
#define dsDrawCylinder dsDrawCylinderD
#define dsDrawCapsule dsDrawCapsuleD
#define dsDrawConvex dsDrawConvexD
#endif

static dWorldID world; //ODE世界のID
//dWorld * world;

static dSpaceID space;
static dGeomID  ground;
static dBodyID koma_body1;
static dGeomID koma_geom1;
static dGeomID ziku_geom1;

dJointGroupID contactgroup;
//dJointGroup contactgroup;
// ここで以下のコードなら main関数で contactgroup = dJointGroupCreate(0);
// として 衝突のときに使用される接触点グループを生成しなければいけないが。
// コメントアウトのコードでは いきなり接触点グループを作成しているみたい。
static dJointID joint_ball;
static dJointID joint_hinge;
static dJointID fixed;

dsFunctions fn;

//コマ本体、軸の各寸法
//コマは円筒 軸はカプセルを使う
const dReal zikuradius = 0.05f;        //pinradiusを zikuradiusに変更、以下同様
const dReal zikulength = 1.5f;
const dReal koma_bodyradius = 1.0f;    //topradiusを koma_bodyradiusに変更、以下同様
const dReal koma_bodylength = 0.25f;
const dReal koma_bodymass = 1.0f;


//台2と床との間
const dReal dai2_dsps = 0.02f;
//台1と台2との隙間
const dReal dai1_dsps = 0.02f;
//軸と台1との隙間
const dReal ziku_dsps = 0.02f;

//台2
const dReal dai2_xl = 0.05f;
const dReal dai2_yl = 0.05f;
const dReal dai2_zl = 2.00f;
const dReal dai2_sides[3] = {dai2_xl, dai2_yl, dai2_zl};

//台1
const dReal dai1_xl = 0.05f;
const dReal dai1_yl = 0.05f;
const dReal dai1_zl = 0.30f;
const dReal dai1_sides[3] = {dai1_xl, dai1_yl, dai1_zl};

//台2位置
const dReal dai2_xpos = 0.0f;
const dReal dai2_ypos = 0.0f;
//const dReal dai2_zpos = 0.45f;        //台2とコマの軸の「隙間」を0.2とする
const dReal dai2_zpos = dai2_dsps + dai2_zl/2 ;

//台1位置
const dReal dai1_xpos = 0.0f;
const dReal dai1_ypos = 0.0f;
//const dReal dai1_zpos = 0.75f;        //台1とコマの軸の「隙間」を0.2とする
const dReal dai1_zpos = dai2_zpos + dai2_zl/2 + dai1_dsps + dai1_zl/2;
//const dReal dai1_zpos = dai2_zpos + dai2_zl/2 + dai1_dsps + dai1_zl/2;

//コマの位置
const dReal koma_xpos = 0.0f;
const dReal koma_ypos = 0.0f;
const dReal koma_zpos = dai1_zpos + dai1_zl/2 + ziku_dsps
                                            + zikuradius + zikulength/2;
//const dReal koma_zpos = 1.80f;      //空中の固定台1上でコマを回転させよう
//const dReal koma_zpos = 0.801f;
//コマの軸のgeomはkomaのbodyに関連づけられている。軸はカプセルであるから
//コマの中心からコマの軸最下部までは、z方向で1.5/2+0.05 で0.80 となる。
//台1とコマの軸最下部をボールジョイントする時の、ジョイントの位置

//台1と軸をボールジョイントするときの、ジョイントの位置
const dReal jb_xpos = 0.0f;
const dReal jb_ypos = 0.0f;
const dReal jb_zpos = dai1_zpos + dai1_zl/2 + ziku_dsps/2;
//const dReal jb_zpos = 0.9f;        //ジョイントの位置を「隙間」の真ん中にする

//台1と台2をヒンジジョイントする時の、ジョイントの位置
const dReal jh_xpos = 0.0f;
const dReal jh_ypos = 0.0f;
const dReal jh_zpos = dai2_zpos + dai2_zl/2 + dai1_dsps/2;
//const dReal jh_zpos = 0.6f;        //ジョイントの位置を「隙間」の真ん中にする

const dReal H_ANGLE = M_PI/2 ;  //ヒンジジョイントを90度に固定

#define MAX_CONTACTS 4
const int N = 1;//描画するコマの数

typedef struct {
  dBodyID body; //動力学計算用のボディ
  dGeomID geom; //衝突計算用のジオメトリ
} MyObject;
MyObject koma[N], ziku[N], dai1[N], dai2[N];//球を描画するための配列

static void nearCallback (void *data, dGeomID o1, dGeomID o2)
{
    // for drawing the contact points
    dMatrix3 RI;
    dRSetIdentity (RI);        //行列RIを単位行列にセットする。回転していない状態に
                                    //する
    //const dReal ss[3] = {0.02,0.02,0.02};
    const dReal ss[3] = {0.2,0.2,0.2};
    // 94行あたりで if (show_contacts) 床とコマの接触面を表示するdsDrawBoxdの大
    // きさ
    //サイズが小さくてわからないので、元コードより大きくした。

    dBodyID b1 = dGeomGetBody(o1);
    dBodyID b2 = dGeomGetBody(o2);

    dContact contact[MAX_CONTACTS];
    //以下接触点の数をnumcで得ている。定番のコード
    int numc = dCollide (o1,o2,MAX_CONTACTS,&contact[0].geom,
                sizeof(dContact));

   //  dContact contact[MAX_CONTACTS];
   //  //接触情報を格納する配列
   //  int numc = dCollide(
   //         o1,
   //         o2,
   //         MAX_CONTACTS,     //接触点の最大数
   //         &contact[0].geom, //接触情報を格納するdContactGeom構造体
   //         sizeof(dContact)  //引数はここはdContactかdContactGeomでいいみたい
   //         );
   //  //dCollide: 接触情報を作成する 戻り値は接触点の数 衝突しない場合は0

    //接触していたら 
    for (int i=0; i<numc; i++) {
        contact[i].surface.mode = dContactApprox1;
       //接触面の性質を設定する
        // http://www.koj-m.sakura.ne.jp/ode/index.php?%CB%E0%BB%A4%A1%F5%C0%DC%BF%A8%CA%D4
        //『dContactApprox1』のフラグを追加する.その 場合,『surface.mu=1』に対応
        //する力まで物体は滑らない.それに対し『dContactApprox1』のフラグを立てな
        //い場合は,静摩擦は 無く,『surface.mu=1』に対応する動摩擦力のみ適用され
        //る.
        // http://hhhappyyyymoto.blogspot.jp/2015/10/so-zou.html?zx=fd636d6421be3cef
        //ode 摩擦 dContactApprox1 Blogger
        //要するに 摩擦が大事なプログラムでは  dContactApprox1にしたらいい?
        contact[i].surface.mu = 2;
       //摩擦係数(摩擦方向1)
        //最大値は1ではないか?

        dJointID c = dJointCreateContact(world,contactgroup,contact+i);
        //contact+iは&contact[i]と同じか?
        dJointAttach (c,b1,b2);
       //接触ジョイントの作成
        //if (show_contacts)
        //    dsDrawBox (contact[i].geom.pos, RI, ss);
        //    //dsDrawBox( dBodyGetPosition( box ), dBodyGetRotation( box ),
        //    //box_sizes );
    }
}


// start simulation - set viewpoint
static void start()
{
  static float xyz[3] = {4.777f, -2.084f, 2.18f};
  static float hpr[3] = {153.0f, -14.5f, 0.0f};
  dsSetViewpoint (xyz,hpr);
  printf ("SPACE to reset\n");                                    //リセット
  printf ("A to tilt the koma_bodys. katamuku\n");            //こまを傾ける
  printf ("T to toggle showing the contact points. sessyoku_tenn_hyouzi\n");//床とコマの接触点を表示
  printf ("1 to save the current state to 'state.dif'.\n");    //ファイルに保存
}

char locase (char c)    //大文字を小文字に変換
{
  if (c >= 'A' && c <= 'Z') return c - ('a'-'A');
  else return c;
}

// called when a key pressed
static void reset();
static void tilt();    //tilt 意味は傾斜 傾ける など

static void command (int cmd)
{
    cmd = locase (cmd);
    if (cmd == ' ')
    {
        reset();
    }
    else if (cmd == 'a') {
        tilt();
    }
    //else if (cmd == 't') {
    //    show_contacts = !show_contacts;
    //}
    //elsh if (cmd == '1') {
    //    write_world = true;
    //}
}

// ヒンジジョイントの制御
static void controlHinge(dReal target)
//void control()
{
    dReal velCoeff = 1.0;    // 比例定数
    dReal p;                        //現在の関節角度
    static dReal angle = target;
   
    p =  dJointGetHingeAngle(joint_hinge);            // 現在の関節角度の取得
    dReal z = angle  -  p;                                // angleは目標角度[rad]
    //if (p >   0.25 * M_PI)    angle  = - 0.25 * M_PI;    // 目標角度の下限
    //if (p < - 0.25 * M_PI)    angle  =    0.25 * M_PI;    // 目標角度の上限
   
    dJointSetHingeParam(joint_hinge, dParamVel,  velCoeff*z);// 目標角速度[rad/s]の設定
    dJointSetHingeParam(joint_hinge, dParamFMax, 100);            // 最大トルク[Nm]の設定
}

// simulation loop
static void simLoop (int pause)
{
    dsSetColor (0,0,2);
    dSpaceCollide(space,0,&nearCallback);
    //space->collide(0,&nearCallback);
   
    if (!pause){
        dWorldStep(world,0.02);
        //world->step(0.02);
        controlHinge(H_ANGLE);   // ヒンジジョイントの制御
        //control();
    }

    dJointGroupEmpty(contactgroup);
    //contactgroup.empty();

    //以下コマの本体 軸 を描画
    dsSetColor (1.0, 0.0, 0.0);                    //赤

    for(int i=0;i<N;i++){
        dsDrawCylinder( dBodyGetPosition(koma[i].body),
                            dBodyGetRotation(koma[i].body),
                            koma_bodylength, koma_bodyradius );
        dsDrawCapsule( dBodyGetPosition(koma[i].body),
                            dBodyGetRotation(koma[i].body),
                            zikulength, zikuradius );

        dsSetColor (0.0, 1.0, 0.0);            //緑
        dsDrawBox( dBodyGetPosition(dai1[i].body),
                            dBodyGetRotation(dai1[i].body),
                            dai1_sides);

        dsSetColor (0.0, 0.0, 1.0);            //青
        dsDrawBox( dBodyGetPosition(dai2[i].body),
                            dBodyGetRotation(dai2[i].body),
                            dai2_sides);

        ////以下コマの本体 軸 を描画
        //dsDrawCylinder(koma_body1->getPosition(),
        //                koma_body1->getRotation(),
        //                koma_bodylength, koma_bodyradius);
        //dsDrawCapsule(koma_body1->getPosition(),
        //                koma_body1->getRotation(),
        //                zikulength, zikuradius);
    }   
}



static void reset()
{
    dMatrix3 R;            //dMatrix3[ 4 * 3 ]
    dRSetIdentity(R);    //Rを単位行列 にする

    for (int i=0; i<N; i++) {
       // 剛体dBodyIDの姿勢を回転行列Rに設定する。
        dBodySetRotation(koma[i].body, R);
        //koma_body1->setRotation(R);
       
        dBodySetPosition(koma[i].body, koma_xpos, koma_ypos, koma_zpos);
        //dBodySetPosition(koma[i].body, 0, 0, 0.801f);
        //koma_body1->setPosition(0.8f, 2, 2);
        dBodySetPosition(dai1[i].body, dai1_xpos, dai1_ypos, dai1_zpos);
        dBodySetPosition(dai2[i].body, dai2_xpos, dai2_ypos, dai2_zpos);

        //軸と台1をボールジョイント
        joint_ball = dJointCreateBall (world,0);
        dJointAttach (joint_ball, koma[i].body, dai1[i].body);
        dJointSetBallAnchor (joint_ball, jb_xpos, jb_ypos, jb_zpos);

        //台1と台2をヒンジ
        joint_hinge = dJointCreateHinge(world, 0);   // ジョイントの作成
        dJointAttach(joint_hinge, dai1[i].body, dai2[i].body );     // ジョイントで2つのボディを接続
        dJointSetHingeAnchor(joint_hinge, jh_xpos, jh_ypos, jh_zpos);  // 中心点の設定
        dJointSetHingeAxis(joint_hinge, 1, 0, 0);    // 回転軸の設定

        //台2を空中で固定ジョイント
        fixed = dJointCreateFixed(world, 0);
        dJointAttach(fixed, NULL, dai2[i].body);   // 固定したいボディをNULLと接続
        dJointSetFixed(fixed);                 // Fixedジョイント設定

        ////台1を空中で固定ジョイント
        //fixed = dJointCreateFixed(world, 0);
        //dJointAttach(fixed, NULL, dai1[i].body);   // 固定したいボディをNULLと接続
        //dJointSetFixed(fixed);                 // Fixedジョイント設定
       
        dBodySetAngularVel( koma[i].body, 0,0,5);
        //bodyの角速度を絶対座標系の各軸周りに(x, y, z) [rad/s]と設定します
        // 上がコマが回る源の関数だろう
        //koma_body1->setAngularVel(0,0,5);
       
        //dBodySetLinearVel(koma[i].body, 0,0.2f,0);
        //これは、殺しておこう
        //koma_body1->setLinearVel(0,0.2f,0);
        //bodyの速度を絶対座標系で(x, y, z) [m/s]に設定します
        //剛体の速度の設定はdBodySetLinearVel()、角速度の設定は
        //dBodySetAngularVel()、を使います。ただし、この速度は重心の速度、角速度は重
        //心周りの角速度です。また、ボディの速度を設定することは初期状態だけにし、時
        //間ステップ毎に強制的にある速度を設定することは、実際の物理現象とは異なった
        //挙動を生み出してしまいますので避けてください。
        //もし、物体の速度をコントロールした場合は、関節に付属しているモータを使って
        //ください   http://demura.net/9ode/481.html
        // 上からの意味だと初期にy軸方向に少しふらつかせている。
        // なんでかな?
        //
        //koma_body1->setLinearVel(0,0.9f,0); とやるとこま2(緑色)が最初からぶっ飛
        //んだ。 このコードを殺すとゆっくりまわっても倒れることがない。
        //自然の状態ではありえない!!!!!!!!!!!!!!

        //dRFromAxisAndAngle(R, 1.0, 0.0, 0.0, M_PI/4);
        //x軸周りに90度回転
        dBodySetRotation(koma[i].body, R);
    }
}

static void tilt()
{
    for(int i=0;i<N;i++){
        dBodyAddTorque(koma[i].body, 0, 10, 0);
        //koma_body1->addTorque(0, 10, 0);
        //原点から手前方向にx 右にy //上にz
        //ボディの重心を作用点にして絶対座標(0, 10, 0)のベクトルのトルクを加える。
        //ということは、手前方向に倒れるトルクが働く
        //以下トルクの定義
        //http://www.washimo-web.jp/Technology/Statics/No06/Statics06.htm
        //何の意味が?
        //上はaキーで実行されるが、やってもただ単にコマをふらつかせて遊んでいるだけ
    }
}

void makeKoma()
{
    for(int i=0;i<N;i++){
        koma[i].body = dBodyCreate(world);
        //koma_body2 = new dBody(world);

        dMass m;
        dMassSetZero(&m);

        //コマ本体
        dMassSetCylinderTotal(&m, 1, 3, koma_bodyradius, koma_bodylength);
       // ⑪シリンダー型の質量をセット
        //m.setCylinderTotal(1, 3, koma_bodyradius, koma_bodylength);
       
        // OdeMass::set_cylinder_total(float total_mass, int direction, float radius, float length);
        // direction:方向
        // @@dMassSetCylinderTotal(this,total,direction,radius,length) と同じ?
        // google で検索してもヒットしない。
        // void dMassSetCapsuleTotal (
       //          dMass *mass,
       //          dReal total_mass, // 質量
       //          int direction,    // 長軸方向(1=x軸 2=y軸 3=z軸)
       //          dReal radius,     // 半径
       //          dReal length      // 長さ
       //          );
        
       dBodySetMass(koma[i].body, &m);
        //⑫ボディに質量をセット
        //koma_body2->setMass(m);
        //dGeom *g1, *g2, *ziku1, *ziku2;
        koma[i].geom = dCreateCylinder(space, koma_bodyradius, koma_bodylength);
       // ⑭ジオメトリ作成
        //g1 = new dCylinder(*space, koma_bodyradius, koma_bodylength);
        dGeomSetBody(koma[i].geom, koma[i].body);
       // ⑮ジオメトリgeomをボディbodyに関連付けます.
        //g1->setBody(*koma_body1);

        //コマの台1
        dai1[i].body = dBodyCreate(world);
        dMassSetBoxTotal(&m, 1, dai1_xl, dai1_yl, dai1_zl);
        dBodySetMass(dai1[i].body, &m);
        //dai1[i].geom = dCreateBox(space, dai1_xl, dai1_yl, dai1_zl);
        //dGeomSetBody(dai1[i].geom, dai1[i].body);
       
        //コマの台2
        dai2[i].body = dBodyCreate(world);
        dMassSetBoxTotal(&m, 2, dai2_xl, dai2_yl, dai2_zl);
        dBodySetMass(dai2[i].body, &m);
        //dai2[i].geom = dCreateBox(space, dai2_xl, dai2_yl, dai2_zl);
        //dGeomSetBody(dai2[i].geom, dai2[i].body);

        //コマの軸
        ziku[i].geom = dCreateCapsule(space, zikuradius, zikulength);
        // ⑯軸のジオメトリ作成
        //ziku1 = new dCapsule(*space, zikuradius, zikulength);
        // なんでkoma_body1にg1とziku1のgeomを同時にセットするのか。
        // そんな事が可能なのか?
        // 円柱状のgeomの上下に少しだけ細いカプセル状のgeomが突き出ている事になる
       
        dGeomSetBody(ziku[i].geom, koma[i].body);
        // ⑰軸のボディを作成せずにコマ本体にジオメトリを関連付ける
        //ziku2->setBody(*koma_body2);
       
        dBodySetGyroscopicMode(koma[i].body, 0);
        //koma_body2->setGyroscopicMode(false);
        // コマを姿勢制御モードにする
        //上をコメントアウトするとこまが倒れやすい
    }
}

//描画の初期化
void  prepDrawStuff()
{  
    fn.version = DS_VERSION;
    fn.start = &start;
    fn.step = &simLoop;
    fn.command = &command;
    fn.stop = 0;
    fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH;
}

int main (int argc, char **argv)
{
    prepDrawStuff();
    dInitODE();
    space = dSimpleSpaceCreate (0);
    // ⑥dSpace型のオブジェクトを生成(衝突空間の生成)
    //space = new dSimpleSpace(0);
    contactgroup = dJointGroupCreate(0);
   ground = dCreatePlane(space,0,0,1,0);
    // ⑦
    //dPlane *floor = new dPlane(*space, 0,0,1,0);
   
    world = dWorldCreate();
    //world = new dWorld();
    dWorldSetGravity(world,0,0,-0.5f);
    //world->setGravity(0,0,-0.5f);
   
    dWorldSetCFM (world, 1e-5f);
    //world->setCFM(1e-5f);        //@eCFMを設定 弱い拘束にしているのか?
    // http://demura.net/9ode/351.html 拘束について
    // 小さな値では強い (hard) 拘束となり、大きな値では弱い (soft) 拘束となりま
    // 既定値     10^-10 (単精度では10^-5)  範囲     10^-9 ~ 1
   
    dWorldSetLinearDamping(world, 0.00001f);
    // ⑤速度減衰率を設定0~1の間 デフォルトは0 つまり減衰しない?
    //world->setLinearDamping(0.00001f);

     dWorldSetLinearDamping(world, 0.00001f);
    //角速度減衰率を設定
    //world->setAngularDamping(0.0001f);
    //上の2つの設定、限りなく0に近いので設定しなくてもいいのでは?
   
    //こまの生成
    makeKoma();
    reset();
    dsSimulationLoop (argc,argv,512,384,&fn);

    dJointGroupEmpty(contactgroup);
    //contactgroup.empty();

    dWorldDestroy( world );                 // 世界の破壊
    //delete koma_body2;
    //delete space;
    //delete world;
    //なんで手動でdeleteするのか?以下ではだめか?
    //スペースのクリーンナップモードを有効にすることで、スペースを破棄したときに
    //そこに含まれるジオメトリも自動的に破棄されるようになります。これは既定で有
    //効となっています。
    //dGeomDestroy ( dGeomID geom )k            //ジオメトリの破棄
    //dJointGroupDestroy( contactgroup ); // ジョイントグループの破棄
   //dSpaceDestroy( space );                 // スペースの破棄
   //dWorldDestroy( world );                 // 世界の破壊
    //
    // 私ばかよねー オバカさんよねー
    //http://www.flow.cs.is.nagoya-u.ac.jp/hamada/programming/cpp5.html
    //配列の動的確保
    // new をしたら deleteをしないとだめ
   
    dCloseODE();
}

0 件のコメント:

コメントを投稿

About

参加ユーザー

連絡フォーム

名前

メール *

メッセージ *

ページ

Featured Posts