北陽電機のスキャナ式レンジセンサUTM-30LXのシミュレータを作りました.URGのAPIを実装していないので不完全です. ODEでのRAYジオメトリの使い方は参考になると思います.
ここからソースコードを取得できます.ほとんどテストしていないので間違いがあるかもしれません.無保証です.自由に使ってください.ここからソースコードlocalization100731.zipをダウンロードできます.Windows用Codeblocksのプロジェクトファイルしかありません.
以下,関係のある部分のソースコードを紹介します.
/*** レーザスキャナの作成 ***/
static void makeLaserScanner()
{
dMass mass;
// レーザスキャナ
laser.body = dBodyCreate(world);
dMassSetZero(&mass);
dMassSetCylinderTotal(&mass,LASER_M,3,LASER_R, LASER_L);
dBodySetMass(laser.body,&mass);
dBodySetPosition(laser.body,START_X, START_Y, START_Z+NECK_L+SENSOR_L+LASER_L/2);
// レーザビーム.Rayジオメトリで実装.ボディはない.
for (int i=0; i < BEAM_NUM; i++)
{
beam[i] = dCreateRay(space, 0);
dGeomRaySetLength(beam[i], BEAM_LENGTH);
}
}
/*** レーザー光の発射 ***/
static void emitLaser()
{
dMatrix3 R1, R2, R3, R4, R5;
const dReal *p = (const dReal *) dBodyGetPosition(laser.body);
dReal heading_angle = -heading();
dReal pitch = dJointGetHingeAngle(laser_joint);
dRFromAxisAndAngle(R1,1,0,0, -M_PI/2);
dRFromAxisAndAngle(R4,1,0,0, pitch);
for (int i=urg.first_index; i < urg.last_index; i+=urg.skip_lines)
{
for (int j=0; j < 3; j++)
{
contact_pos[i][j] = -1;
}
dRFromAxisAndAngle(R2,0,0,1,
heading_angle +(double) (i - (BEAM_NUM/2))/(BEAM_NUM/2) * LASER_LO_LIMIT * (M_PI/180));
dMultiply0(R3,R2,R1,3,3,3);
dMultiply0(R5,R4,R3,3,3,3);
dGeomSetPosition(beam[i], p[0], p[1], p[2]);
dGeomSetRotation(beam[i],R5);
}
}
/*** レーザの描画 ***/
void drawLaser()
{
const dReal *pos;
dsSetColorAlpha(1.0, 0.0, 0.0, 0.5);
static int skip = 6; // 描画を高速にするための間引き数
pos = dBodyGetPosition(laser.body);
for (int i = 0; i < BEAM_NUM; i++)
{
if (!((contact_pos[i][0] == -1) && (contact_pos[i][1] == -1) &&
(contact_pos[i][2] == -1)))
{
// 描画を高速にするために間引く
if ((i % skip) == 0)dsDrawLine(pos,(const double *) contact_pos[i]);
}
}
}
/*** コールバック関数 ***/
static void nearCallback (void *data, dGeomID o1, dGeomID o2)
{
int i,n;
dBodyID b1 = dGeomGetBody(o1);
dBodyID b2 = dGeomGetBody(o2);
if (b1 && b2 && dAreConnectedExcluding(b1,b2,dJointTypeContact)) return;
static const int N = 10;
dContact contact[N];
n = dCollide(o1,o2,N,&contact[0].geom,sizeof(dContact));
if (n > 0)
{
for (i=0; i<n; i++)
{
if ((o1 == wheel[FRONT].geom) || (o2 == wheel[FRONT].geom) ||
(o1 == wheel[REAR].geom) || (o2 == wheel[REAR].geom))
{
contact[i].surface.mode = dContactSoftERP | dContactSoftCFM | dContactApprox1;
//contact[i].surface.mode = dContactSoftERP | dContactSoftCFM ;
contact[i].surface.mu = 0.0; // dInfinity; // 摩擦係数0
contact[i].surface.soft_erp = 1.0; // 地面のERP
contact[i].surface.soft_cfm = 1e-5; // 地面のCFM
}
else
{
// レーザビームの衝突判定
if (dGeomGetClass(o1) == dRayClass || dGeomGetClass(o2) == dRayClass)
{
for (int k=0; k < BEAM_NUM; k++)
{
if (o1 == beam[k] || o2 == beam[k])
{
// 物体をつきなけないために
if ((contact_pos[k][0] == -1) && (contact_pos[k][1] == -1)
&& (contact_pos[k][2]==-1))
{
for (int j=0; j < 3; j++)
contact_pos[k][j] = contact[i].geom.pos[j];
}
else
{
return;
}
}
}
return; // 接触点を見つけるだけなので戻る
}
contact[i].surface.mode = // dContactSlip1 | dContactSlip2 |
dContactSoftERP | dContactSoftCFM | dContactApprox1 | dContactMu2;
contact[i].surface.mu = 1.0; // dInfinity; // 摩擦係数無限大
contact[i].surface.slip1 = 0.001; // 第1摩擦方向の滑り
contact[i].surface.slip2 = 0.001; // 第2摩擦方向の滑り
contact[i].surface.soft_erp = 1.0; // 地面のERP
contact[i].surface.soft_cfm = 1e-5; // 地面のCFM
}
dJointID c = dJointCreateContact(world,contactgroup,&contact[i]); //接触ジョイントの生成
dJointAttach(c,b1,b2); // ジョイントの結合
}
}
}
/*** レーザのデータ取得 ***/
void laser_receiveData(laser_t *urg, long data[], int data_max)
{
const dReal *pos;
pos = dBodyGetPosition(laser.body);
for (int i = 0; i < BEAM_NUM; i++)
{
if (!((contact_pos[i][0] == -1) && (contact_pos[i][1] == -1) &&
(contact_pos[i][2] == -1)))
{
// 描画を高速にするために間引く
dReal d2 = 0;
for (int j = 0; j < 3; j++)
{
d2 += (pos[j]-contact_pos[i][j]) * (pos[j] - contact_pos[i][j]);
}
data[i] = (long) (1000 * sqrt(d2));
// printf("Laser dist[%d]=%.2f \n",i,laser_dist[i]);
}
else
{
data[i] = -1;
}
}
}
void laser_setSkipLines(laser_t *urg, int skip_lines)
{
urg->skip_lines = skip_lines;
}
void laser_requestData(laser_t *urg, laser_request_type request_type, int first_index, int last_index)
{
if ((first_index < 0)||(first_index > BEAM_NUM)|| (first_index > last_index))
{
printf("first index error \n");
}
else
{
urg->first_index = first_index;
}
if ((last_index < 0) || (last_index > BEAM_NUM) || (last_index < first_index))
{
printf("last index error \n");
}
else
{
urg->last_index = last_index;
}
}
/*** シミュレーションループ ***/
static void simLoop(int pause)
{
double vel = 10.0;
static double laser_angle = 0;
static double diff = 1;
emitLaser(); // レーザー光発射
if (!pause)
{
controlWheel(v_r, v_l);
if ((laser_angle >= M_PI/4) || (laser_angle <= - M_PI/4))
{
diff *= -1;
}
swingLaser(laser_angle += 5.0 * diff * M_PI/180.0);
deadReckoning();
//navigation(vel);
dSpaceCollide(space,0,&nearCallback);
dWorldQuickStep(world, STEP_SIZE);
dJointGroupEmpty(contactgroup);
}
laser_receiveData(&urg, laser_data,laser_data_max);
//for (int i=0; i < laser_data_max;i++) {
// printf("Laser dist[%d]=%d \n",i,laser_data[i]);
//}
//drawParticles();
drawRobot();
if (show) drawLaser();
drawWall();
steps++;
}
/*** レーザスキャナの初期化 ***/
void initLaser()
{
laser_data = (long*)malloc(sizeof(long) * laser_data_max);
if (laser_data == NULL)
{
perror("malloc");
exit(1);
}
laser_setSkipLines(&urg,2);
laser_requestData(&urg, URG_MD, URG_FIRST, URG_LAST);
}
/*** メイン関数 ***/
int main(int argc, char *argv[])
{
setDrawStuff(); // 描画関数の設定
srand(time(NULL));
dInitODE();
world = dWorldCreate(); // ワールドの生成
space = dHashSpaceCreate(0); // スペースの生成
contactgroup = dJointGroupCreate(0); // 接触点グループの生成
ground = dCreatePlane(space,0,0,1,0); // 地面の生成
dWorldSetGravity(world, 0, 0, -9.8); // 重力加速度の設定
//dWorldSetCFM(world,1e-5); // CFMの設定
dWorldSetERP(world,1.0); // ERPの設定 0.8
makeRobot(); // ロボットの生成
makeWall(); // 壁の生成
initLaser(); // レーザスキャナの初期化
dsSimulationLoop(argc,argv,800,480,&fn); // シミュレーションループ
dJointGroupDestroy(contactgroup); // 接触点グループの破壊
dSpaceDestroy(space); // スペースの破壊
dWorldDestroy(world); // ワールドの破壊
dCloseODE();
return 0;
}

コメント