ODEで学ぶC言語2のStep5です.今回は構造体と物体へ力やトルクを加える方法,さらにシミュレーションのリセット法などを学びます.構造体の概要については既にわかっているものとし,サンプルコードを示すことにより具体的な使い方を学びます.
○ 構造体
配列では同じ型しかまとめることができませんでしたが,構造体では違う型をまとめて扱うことができるのでシミュレーションなど物体に多くの属性がある場合に便利です.このサンプルプログラムでは物体をdmObjectという構造体で次のように表しています.dm5.hの16行目に定義しています.今回の例はドミノ倒しなので,この構造体を元にドミノを21個生成しています.
typedef struct{
dBodyID body; // ボディのID
dGeomID geom; // ジオメトリのID
const double *p; // x, y, z [m]
const double *R; // 回転行列 要素数4x3
double m; // 質量 [kg]
double r,l; // 半径 [m], 長さ [m]
const double *side; // サイズ x,y,z
const double *color; // 色 r,g,b
} dmObject;
○ 姿勢の変更
物体の姿勢を変更するためには次のAPIを使い回転行列Rの値を変更します.ここで,Rは回転行列が格納されている配列へのポインタ,ax, ay, axは回転軸ベクトル.angleは回転角度となります.なお,dだけで始まるAPIはODEのAPIです.
- dRFromAxisAndAngle(double R[12], double ax, doulbe ay, double az, double angle);
○ 力,トルクの加え方
- void dmAddForce(dmObject *obj, double fx, double fy, double fz)
- 物体objの重心に力(fx,fy,fz)を加える
- void dmAddTorque(dmObject *obj, double fx, double fy, double fz)
- 物体objの重心にトルク(fx, fy, fz)を加える.fx,fy,fzはそれぞれx, y, z軸まわりのトルク
○ 高速なシミュレーション
前回のサンプルではシミュレーションのステップ関数としてdmSimStep()を使いましたが,ここではより高速なdmSimQuickStep()を使います.ただし,dmSimStep()と比較して精度が悪くなります.
○ シミュレーションのリセット
rまたはRキーを押すと,resetSim関数が呼ばれてシミュレーションがリセットされます.resetSimの中身はシミュレーションループが1回以上呼び出されたときにdmInit関数を呼び出して初期化し,ドミノを再度生成しています.
○ ソースコード
/* step5 ドミノ倒し */
#include "dm5.h"
#define DOMINO_NUM 21
static int STEPS = 0; // シミュレーションのステップ数
double red[3] = {1.3, 0.0, 0.0}; // 赤色
dmObject domino[DOMINO_NUM]; // ドミノ
void simLoop(int pause) /*** シミュレーションループ ***/
{
int i;
double x = 5.0, y = 0, z = 1; // カメラの位置[m]
double yaw = -180, pitch = 0, roll = 0; // カメラの姿勢 ヨー,ピッチ,ロール角[°]
if (STEPS == 0) dmSetCamera(x,y,z,yaw,pitch,roll); // カメラの設定
dmSimQuickStep(); // シミュレーションを1ステップ進める(高速版)
for (i = 0; i < DOMINO_NUM; i++)
{
dmDraw(&domino[i]); // 壁の描画
}
STEPS++;
}
void resetSim(int n)
{
double m = 0.1; // 質量
double side[3] = {0.2, 0.05, 0.5}; // サイズ
double R[12] = {1,0,0,0, 0,1,0,0, 0,0,1,0}; // 姿勢
double p[DOMINO_NUM][3]; // 位置
int i;
// シミュレーションの終了
if (STEPS != 0)
{
dmClose();
}
dmInit(); // 初期化
// ドミノの生成
for (i = 0; i < DOMINO_NUM; i++)
{
p[i][2] = 0.25; // ドミノ重心のz座標
switch (n)
{
case 0:
p[i][0] = 0; // ドミノ重心のx座標
p[i][1] = 0.3 * i -3.0; // ドミノ重心のy座標
break;
case 1:
p[i][0] = 0.3 * i -3.0;
p[i][1] = p[i][0];
// z軸周りにπ/4回転させた姿勢
dRFromAxisAndAngle(R, 0, 0, 1, - M_PI/4);
break;
default:
printf("Bad number \n");
break;
}
dmCreateBox(&domino[i], p[i], R, m, side, red);
}
}
void command(int cmd)
{
switch (cmd)
{
case '1':
resetSim(1);
break;
case 'r':
case 'R':
resetSim(0);
break;
case 'f':
case 'F':
{
double fx = -1.0, fy = 0.0, fz = 0.0;
dmAddTorque(&domino[0], fx, fy, fz); // x,y,z軸まわりにfx,fy,fzのトルクを付加
break;
}
default:
printf("Input r, R, f, F key \n");
break;
}
}
/*** main関数 ***/
int main()
{
resetSim(0); // シミュレーションのリセット
dmLoop(800, 600, simLoop, command); // ウインドウの幅,高, ループ関数,コマンド関数
dmClose(); // 終了
return 0;
}
ホームワーク
- サンプルコードstep5-100720.zipをここからダウンロードして実行しよう.
- ドミノを X状に配置して,ドミノ倒しをしなさい. 解答例step5b-100726.zip
- ドミノを半径2mの円上に配置して,ドミノ倒しをしなさい
- 上の配置でドミノ倒しが無限に続くように,ドミノがひとりでに起き上がるようにしなさい.

コメント