2014年8月16日土曜日

ODEでサイコロのシミュレーション


こんにちは。
突然ですが今日のテーマはODEを使ったちょっとしたプログラムです。
ある日、研究室の隣の人がサイコロを振って、出た目の数×10分ずつしっかり集中するという変なゲームを始めました。
そこでODE(Open Dynamics Engine)を使い、サイコロを振って出た目を判定するシミュレーションを行ってちゃちゃっと勉強時間を決めてしまいましょう!というものです。
サイコロはODEで描画できる基本図形(立方体と円柱)でサイコロの本体と数字を表す目を表現しました。チマチマと寸法、位置、重心を定義して作成した結果が下の図です。

なんとなくそれっぽいものが出来ました。ソースコードはあとでリンクを貼っておきますが、一つ一つの数字の目を表現するのにコードがとても長くなってしまいました。もうちょっと賢く書けるといいですね。
 あとサイコロを落とすとき落とす姿勢を固定してしまうと、毎回実行して出る目が一緒になってしまいますから乱数をつかってサイコロの姿勢をランダムに決定します。このコードについては下に示します。

//引数:手に入れたい乱数の範囲(minからmax)で少数点第何位(digit)まで求めるか
double GetRandom(double min,double max, int digit){
  double ten,R;
  static int srand_flag = 0;

  if (srand_flag == 0) {
    srand((unsigned int)time(NULL));
    srand_flag = 1;
  }

  ten = pow(10, digit-1);
  R = min*ten + (int)(rand()*((max-min)*ten+1.0)/(1.0+RAND_MAX));
  return R/ten;
}


void Randam()
{
  dReal theta[4];

  for (int i = 0; i < 4; i++) {//※注意1
    theta[i] = M_PI*GetRandom(-2,2,4);//-2piから2piまでの角度をランダムに決定
  }
  dRFromEulerAngles(R, theta[1], theta[2], theta[3]);//サイコロの姿勢の回転行列をオイラー角で決定
}

※注意1:Macで実行したところ配列の一番目の要素が変化しないという不都合な現象が起きたのでひとつ余分に取得して一番目の要素を無視して使っています。
参考: C言語の乱数発生について質問です。
http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1258942042
これで高いところから落とすだけでコロコロっとランダムに転がりますから本来の目的は達成できるのですが、あと人間の目で出た目を判別するだけではなく、プログラムでもちゃんと転がったあと何が出たのかを判定するコードを書きました。これについては該当する関数についてコードを下に示します。

int detame()
{
  const dReal *linear_vel;
  const dReal *pos1, *pos2, *pos3, *pos4, *pos5, *pos6;
  double pos[6];
  double max = 0.0;
  int max_num = 0;

  linear_vel  = dBodyGetLinearVel(dice.body);//サイコロのそれぞれx,y,z方向の速度を取得

  if(cnt>=300 && linear_vel[0] <= 0.00000001 && linear_vel[1] <= 0.00000001 && linear_vel[2] <= 0.00000001 ){
  //cntはsimloopが何回呼び出されたかのカウンタ、これが300カウントを超えたかつ、x,y,z方向の速度がものすごく小さくなったら
  //サイコロの目の位置を1から6の目に関してゲット
    pos1 = dBodyGetPosition(one.body);
    pos2 = dBodyGetPosition(two[0].body);
    pos3 = dBodyGetPosition(three[0].body);
    pos4 = dBodyGetPosition(four[0].body);
    pos5 = dBodyGetPosition(five[0].body);
    pos6 = dBodyGetPosition(six[0].body);

  //z座標のみ取り出して別の配列に格納
    pos[0] = pos1[2];//1の目のz座標
    pos[1] = pos2[2];//2の目のz座標
    pos[2] = pos3[2];//3の目のz座標
    pos[3] = pos4[2];//4の目のz座標
    pos[4] = pos5[2];//5の目のz座標
    pos[5] = pos6[2];//6の目のz座標

  //z座標の最大値を求めて一番高い位置にある目を決める
    max = pos[0];
    max_num = 0;
    for(int i = 1; i < 6; i++){
      if(pos[i]>=max){
        max = pos[i];
        max_num = i;
      }
    }
    if(judge == 0){//simloopの中で一回だけ出た目をprintf
      printf("出た目は %d !!\n", max_num+1);
      judge = 1;
    }
  }else{
    max_num = -1;
  }

  return max_num+1;//一応出た目をリターン
}

これでサイコロシミュレータの完成です。これで効率的に勉強ができますね。このサイコロシミュレーターすべてのソースコードは下のリンクからダウンロードできます。
ダウンロードはこちら(Googleドライブ)
動作はODE 0.13でOSはUbuntu 14.04とMac OS X 10.9.4で確認しています。Windowsは動作未確認です。ごめんなさい。またいろいろごちゃごちゃやってますので環境によってはうまく動作しないかもしれません...

以上のことができたらサイコロをもっと増やして遊んでみるのも楽しいかもしれません。サイコロを25個定義してシミュレーションを行った動画です。これだったら勉強集中時間も勝手に長くなりますし。となりのお友達も喜んでくれるでしょう。



また本来の活動とは関係のない内容でしたね。しかしODEではロボットの動作シミュレーションもできたりしますから、関係がない訳ではないかもしれません。
今日はここまでです。ありがとうございました。

参考にさせて頂いたサイト
demura.net: ロボットの開発と教育
http://demura.net/
出村先生、「ロボットシミュレーション」の書籍、サイト共にわかりやすい内容でいつもお世話になっております。ありがとうございます。

0 件のコメント:

コメントを投稿