phymarxbot.cpp
1 /********************************************************************************
2  * WorldSim -- library for robot simulations *
3  * Copyright (C) 2012-2013 *
4  * Gianluca Massera <emmegian@yahoo.it> *
5  * Fabrizio Papi <erkito87@gmail.com> *
6  * *
7  * This program is free software; you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation; either version 2 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program; if not, write to the Free Software *
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
20  ********************************************************************************/
21 
22 #include "phymarxbot.h"
23 #include "phybox.h"
24 #include "phycylinder.h"
25 #include "phyfixed.h"
26 #include "phyballandsocket.h"
27 #include "physphere.h"
28 #include "physuspension.h"
29 #include "phycompoundobject.h"
30 #include "phycone.h"
31 #include "mathutils.h"
32 #include <cmath>
33 
34 namespace farsa {
35 
36 const real PhyMarXbot::basex = 0.034f;
37 const real PhyMarXbot::basey = 0.143f;
38 const real PhyMarXbot::basez = 0.048f;
39 const real PhyMarXbot::basem = 0.4f;
40 const real PhyMarXbot::bodyr = 0.085f;
41 const real PhyMarXbot::bodyh = 0.0055f;
42 const real PhyMarXbot::bodym = 0.02f;
43 const real PhyMarXbot::axledistance = 0.104f;
44 const real PhyMarXbot::trackradius = 0.022f;
45 const real PhyMarXbot::trackheight = 0.0295f;
46 const real PhyMarXbot::trackm = 0.05f;
47 const real PhyMarXbot::treaddepth = 0.004f;
48 const real PhyMarXbot::wheelr = 0.027f;
49 const real PhyMarXbot::wheelh = 0.0215f;
50 const real PhyMarXbot::wheelm = 0.02f;
51 const real PhyMarXbot::attachringh = 0.0285f;
52 const real PhyMarXbot::attachringm = 0.08f;
53 const real PhyMarXbot::ledsh = 0.010f;
54 const real PhyMarXbot::ledsradius = 0.080f;
55 const real PhyMarXbot::ledsm = 0.03f;
56 
57 PhyMarXbot::PhyMarXbot( World* w, QString name, const wMatrix& basetm ) : WObject( w, name, basetm, false ) {
58  kinematicSimulation = false;
59 
60  // --- reference frame
61  // X
62  // ^
63  // | ------
64  // | ______|____|_________
65  // | |_|_| track |_|_|_|_|
66  // | -------------------
67  // | | battery |
68  // | -------------------
69  // | |_|_| track |_|_|_|_|
70  // | | |
71  // | ------
72  // |
73  // ---------------------> Y
74  // The front of the robot is towards -Y (positive speeds of the wheel make the robot move towards -Y)
75 
76  // --- create a material for marXbot
77  w->materials().createMaterial( "marXbotMaterial" );
78  w->materials().setProperties( "marXbotMaterial", "default", 0.0f, 0.0f, 0.01f, 0.01f, true );
79  w->materials().createMaterial( "marXbotTire" );
80  w->materials().setProperties( "marXbotTire", "default", 1.2f, 0.9f, 0.01f, 0.01f, true );
81 
82  // --- create the body of the base of the marxbot
83  QVector<PhyObject*> objs;
85  // battery container
86  PhyObject* obj = new PhyBox( basex, basey, basez, w );
87  tm.w_pos[2] = basez/2.0f + 0.015f;
88  obj->setMass( basem );
89  obj->setMatrix( tm );
90  objs << obj;
91  // cylinder body
92  obj = new PhyCylinder( bodyr, bodyh, w );
93  obj->setMass( bodym );
94  tm = wMatrix::yaw( toRad(90.0f) );
95  tm.w_pos[2] = 0.015f + basez + bodyh/2.0f;
96  obj->setMatrix( tm );
97  objs << obj;
98  // right track
99  obj = new PhyBox( trackheight, axledistance, trackradius*2.0f, w );
100  tm = wMatrix::identity();
101  tm.w_pos[0] = (basex + trackheight)/2.0f;
102  tm.w_pos[2] = trackradius + treaddepth;
103  obj->setMass( trackm );
104  obj->setMatrix( tm );
105  objs << obj;
106  obj = new PhyCylinder( trackradius, trackheight, w );
107  obj->setMass( wheelm );
108  tm.w_pos[1] = axledistance/2.0;
109  obj->setMatrix( tm );
110  objs << obj;
111  obj = new PhyCylinder( trackradius, trackheight, w );
112  obj->setMass( wheelm );
113  tm.w_pos[1] = -axledistance/2.0;
114  obj->setMatrix( tm );
115  objs << obj;
116  // left track
117  obj = new PhyBox( trackheight, axledistance, trackradius*2.0f, w );
118  tm = wMatrix::identity();
119  tm.w_pos[0] = -(basex + trackheight)/2.0f;
120  tm.w_pos[2] = trackradius + treaddepth;
121  obj->setMass( trackm );
122  obj->setMatrix( tm );
123  objs << obj;
124  obj = new PhyCylinder( trackradius, trackheight, w );
125  obj->setMass( wheelm );
126  tm.w_pos[1] = axledistance/2.0;
127  obj->setMatrix( tm );
128  objs << obj;
129  obj = new PhyCylinder( trackradius, trackheight, w );
130  obj->setMass( wheelm );
131  tm.w_pos[1] = -axledistance/2.0;
132  obj->setMatrix( tm );
133  objs << obj;
134  // merge all togheter
135  base = new PhyCompoundObject( objs, w, "base" );
136  base->setMaterial("marXbotMaterial");
137  base->setOwner( this, false );
138  base->setMatrix( basetm );
139 
140  // --- create the two external wheels
141  // they are motorized and they move the robot
142  // and create four spheres for supporting the tracks
143  // NOTE: the tracks are not simulated, they are just rigid bodies and
144  // the four sphere move freely in order to approximate the real movement
145  // --- position of wheels and their IDs in the vector
146  // +-----+
147  // |3|| ||2|
148  // |1| | | |0|
149  // |5|| ||4|
150  // +-----+
151  // --------------------> X
152  // arrays of X and Y offsets for calculate the positions
153  double xoffs[6] = { 1, -1, 1, -1, 1, -1 };
154  double yoffs[6] = { 0, 0, 1, 1, -1, -1 };
155  PhyObject* awheel;
156  PhyJoint* joint;
157  for( int i=0; i<6; i++ ) {
158  // --- relative to base
159  wMatrix wtm = wMatrix::identity();
160  wtm.w_pos = wVector(
161  xoffs[i] * (basex + trackheight)/2.0f + (yoffs[i]==0 ? xoffs[i]*(trackheight/2.0+wheelh/2.0f+0.006f) : 0.0f),
162  yoffs[i] * (axledistance/2.0f),
163  0.0f );
164  if ( i<2 ) {
165  // the two motorized wheels
166  wtm.w_pos[2] = wheelr;
167  awheel = new PhyCylinder( wheelr, wheelh, w, "motorWheel" );
168  } else {
169  // the four sphere supporting the track
170  wtm.w_pos[2] = treaddepth;
171  awheel = new PhySphere( treaddepth, w, "passiveWheel" );
172  }
173  wheelstm.append( wtm );
174  awheel->setMass( wheelm );
175  awheel->setColor( Qt::blue );
176  awheel->setMaterial( "marXbotTire" );
177  awheel->setOwner( this, false );
178  awheel->setMatrix( wtm * basetm );
179  wheels.append( awheel );
180 
181  //--- create the joints
182  if ( i<2 ) {
183  // the two motorized wheels
184  joint = new PhySuspension(
185  base->matrix().x_ax, // rotation axis
186  wheels[i]->matrix().w_pos,
187  base->matrix().z_ax, // suspension 'vertical' axis
188  base, wheels[i] );
189  joint->dofs()[0]->disableLimits();
190  } else {
191  // the four sphere supporting the track
192  joint = new PhyBallAndSocket(
193  wheels[i]->matrix().w_pos,
194  base, wheels[i] );
195  }
196  joint->setOwner( this, false );
197  joint->updateJointInfo();
198  wheelJoints.append( joint );
199  }
200 
201  // --- create the attachment module with the 12 RGB LEDs part
202  objs.clear();
203  obj = new PhyCone( bodyr, attachringh+0.010f, w );
204  obj->setMass( attachringm );
205  tm = wMatrix::identity();
206  tm.w_pos[0] = 0.005f;
207  obj->setMatrix( tm );
208  objs << obj;
209  obj = new PhyCone( bodyr, attachringh+0.010f, w );
210  obj->setMass( attachringm );
211  tm = wMatrix::roll( toRad(180.0f) );
212  tm.w_pos[0] = -0.005f;
213  obj->setMatrix( tm );
214  objs << obj;
215  obj = new PhyCylinder( ledsradius, ledsh, w );
216  obj->setMass( ledsm );
217  obj->setTexture( "marXbot_12leds" );
218  obj->setUseColorTextureOfOwner( false );
219  tm.w_pos[0] = attachringh/2.0f + ledsh/2.0f + 0.0015f;
220  obj->setMatrix( tm );
221  objs << obj;
222  // merge all toghether
223  attachring = new PhyCompoundObject( objs, w );
224  attachring->setMaterial("marXbotMaterial");
225  attachring->setOwner( this, false );
226  attachring->setUseColorTextureOfOwner( false );
227  attachringtm = wMatrix::yaw( toRad(-90.0f) );
228  attachringtm.w_pos[2] = 0.015f + basez + bodyh + attachringh/2.0f + 0.0015f;
229  attachring->setMatrix( attachringtm * basetm );
230  // --- create the joint for sensing the force
231  forcesensor = new PhyFixed( base, attachring, w );
232  forcesensor->setOwner( this, false );
233 
234  // create the motor controller
235  QVector<PhyDOF*> motors;
236  motors << wheelJoints[0]->dofs()[0] << wheelJoints[1]->dofs()[0];
237  wheelsCtrl = new WheelMotorController( motors, w );
238  wheelsCtrl->setOwner( this, false );
239 #ifdef __GNUC__
240  #warning QUI DECIDERE PER LE VELOCITÀ MASSIME E MINIME DELLE RUOTE
241 #endif
242  wheelsCtrl->setSpeedLimits( -10.0, -10.0, 10.0, 10.0 );
243 
244  // Connecting wheels speed signals to be able to move the robot when in kinematic
245  connect(wheelJoints[0]->dofs()[0], SIGNAL(changedDesiredVelocity(real)), this, SLOT(setRightWheelDesideredVelocity(real)));
246  connect(wheelJoints[1]->dofs()[0], SIGNAL(changedDesiredVelocity(real)), this, SLOT(setLeftWheelDesideredVelocity(real)));
247 
248  // Creating the proximity IR sensors
249  QVector<SingleIR> sensors;
250 
251 #ifdef __GNUC__
252  #warning QUI DECIDERE PER I RANGE/APERTURE E LE POSIZIONI (SPECIE PER I GROUND)
253 #endif
254  // Adding the sensors. The marxbot has 24 proximity infrared sensors
255  for (unsigned int i = 0; i < 24; i++) {
256  const double curAngle = double(i) * (360.0 / 24.0) + ((360.0 / 24.0) / 2.0);
257  const double radius = bodyr;
258 
259  wMatrix mtr = wMatrix::yaw(deg2rad(curAngle + 90.0)) * wMatrix::pitch(deg2rad(90.0f));
260  mtr.w_pos.x = radius * cos(deg2rad(curAngle));
261  mtr.w_pos.y = radius * sin(deg2rad(curAngle));
262  mtr.w_pos.z = 0.015f + basez + bodyh/2.0f;
263 
264  sensors.append(SingleIR(base, mtr, 0.02, 0.1, 10.0, 5));
265  }
266  proximityIR = new SimulatedIRProximitySensorController(world(), sensors);
267 
268  // Now creating the ground IR sensors below the battery pack
269  sensors.clear();
270  wMatrix mtr = wMatrix::yaw(deg2rad(180.0));
271  const double distFromBorder = 0.003;
272 
273  // Adding the sensors. The marxbot has 4 ground infrared sensors below the battery pack
274  mtr.w_pos = wVector(basex / 2.0f - distFromBorder, basey / 2.0f - distFromBorder, 0.015f);
275  sensors.append(SingleIR(base, mtr, 0.0, mtr.w_pos.z + 0.005, 0.0, 1));
276  mtr.w_pos = wVector(basex / 2.0f - distFromBorder, -basey / 2.0f + distFromBorder, 0.015f);
277  sensors.append(SingleIR(base, mtr, 0.0, mtr.w_pos.z + 0.005, 0.0, 1));
278  mtr.w_pos = wVector(-basex / 2.0f + distFromBorder, basey / 2.0f - distFromBorder, 0.015f);
279  sensors.append(SingleIR(base, mtr, 0.0, mtr.w_pos.z + 0.005, 0.0, 1));
280  mtr.w_pos = wVector(-basex / 2.0f + distFromBorder, -basey / 2.0f + distFromBorder, 0.015f);
281  sensors.append(SingleIR(base, mtr, 0.0, mtr.w_pos.z + 0.005, 0.0, 1));
282  groundBottomIR = new SimulatedIRGroundSensorController(world(), sensors);
283 
284  // Creating the ground IR sensors below the base
285  sensors.clear();
286 
287  // Adding the sensors. The marxbot has 8 ground infrared sensors below the base
288  for (unsigned int i = 0; i < 8; i++) {
289  const double curAngle = double(i) * (360.0 / 8.0);
290  const double radius = bodyr - 0.003;
291 
292  wMatrix mtr = wMatrix::yaw(deg2rad(180.0));
293  mtr.w_pos.x = radius * cos(deg2rad(curAngle));
294  mtr.w_pos.y = radius * sin(deg2rad(curAngle));
295  mtr.w_pos.z = 0.015f + basez;
296 
297  sensors.append(SingleIR(base, mtr, 0.0, mtr.w_pos.z + 0.005, 0.0, 1));
298  }
299  groundAroundIR = new SimulatedIRGroundSensorController(world(), sensors);
300 
301  // Creating the traction sensor
302  tractionSensor = new TractionSensorController(world(), *forcesensor);
303 
304  w->pushObject( this );
305 }
306 
308  foreach( PhyJoint* susp, wheelJoints ) {
309  delete susp;
310  }
311  foreach( PhyObject* w, wheels ) {
312  delete w;
313  }
314  delete wheelsCtrl;
315  delete forcesensor;
316  delete attachring;
317  delete base;
318  delete proximityIR;
319  delete groundBottomIR;
320  delete groundAroundIR;
321 }
322 
324 {
325  // Updating motors
326  if (wheelsCtrl->isEnabled()) {
327  wheelsCtrl->update();
328  }
329 
330  if (kinematicSimulation) {
331  // In kinematic mode, we have to manually move the robot depending on the wheel velocities
332  wMatrix mtr = matrix();
333 
334  // To compute the actual movement of the robot I assume the movement is on the plane on which
335  // the two wheels lie. This means that I assume that both wheels are in contact with a plane
336  // at every time. Then I compute the instant centre of rotation and move the robot rotating
337  // it around the axis passing through the instant center of rotation and perpendicular to the
338  // plane on which the wheels lie (i.e. around the local XY plane)
339 
340  // First of all computing the linear speed of the two wheels
341  const real leftSpeed = leftWheelVelocity * wheelr;
342  const real rightSpeed = rightWheelVelocity * wheelr;
343 
344  // If the speed of the two wheels is very similar, simply moving the robot forward (doing the
345  // computations would probably lead to invalid matrices)
346  if (fabs(leftSpeed - rightSpeed) < 0.00001f) {
347  // The front of the robot is towards -y
348  mtr.w_pos += -mtr.y_ax.scale(rightSpeed * world()->timeStep());
349  } else {
350  // The first thing to do is to compute the instant centre of rotation. We do the
351  // calculation in the robot local frame of reference. We proceed as follows (with
352  // uppercase letters we indicate vectors and points):
353  //
354  // +------------------------->
355  // | X axis
356  // | ^
357  // | |\
358  // | | \A
359  // | | \
360  // | L| ^
361  // | | |\
362  // | | | \
363  // | | R| \
364  // | | | \
365  // | O+--->----+C
366  // | D
367  // |
368  // |
369  // v Y axis (the robot moves forward towards -Y)
370  //
371  // In the picture L and R are the velocity vectors of the two wheels, D is the vector
372  // going from the center of the left wheel to the center of the right wheel, A is the
373  // vector going from the tip of L to the tip of R and C is the instant centre of
374  // rotation. D is fixed an parallel to the X axis, while L and R are always parallel
375  // to the Y axis. Also, for the moment, we take the frame of reference origin at O.
376  // The construction shown in the picture allows to find the instant center of rotation
377  // for any L and R except when they are equal. In this case C is "at infinite" and the
378  // robot moves forward of backward without rotation. All the vectors lie on the local
379  // XY plane. To find C we proceed in the following way. First of all we note that
380  // A = R - L. If a and b are two real numbers, then we can say that C is at the
381  // instersection of L + aA with bD, that is we need a and b such that:
382  // L + aA = bD.
383  // If we take the cross product on both sides we get:
384  // (L + aA) x D = bD x D => (L + aA) x D = 0
385  // This means that we need to find a such that L + aA and D are parallel. As D is parallel
386  // to the X axis, its Y component is 0, so we can impose that L + aA has a 0 Y component,
387  // too, that is:
388  // (Ly) + a(Ay) = 0 => a = -(Ly) / (Ay)
389  // Once we have a, we can easily compute C:
390  // C = L + aA
391  // In the following code we use the same names we have used in the description above. Also
392  // note that the actual origin of the frame of reference is not in O: it is in the middle
393  // between the left and right wheel (we need to take this into account when computing the
394  // point around which the robot rotates)
395  const wVector L(0.0, -leftSpeed, 0.0);
396  const wVector A(wheelstm[0].w_pos.x - wheelstm[1].w_pos.x, -(rightSpeed - leftSpeed), 0.0);
397  const real a = -(L.y / A.y);
398  const wVector C = L + A.scale(a);
399 
400  // Now we have to compute the angular velocity of the robot. This is simply equal to v/r where
401  // v is the linear velocity at a point that is distant r from C. We use the velocity of one of
402  // the wheel: which weels depends on its distance from C. We take the wheel which is furthest from
403  // C to avoid having r = 0 (which would lead to an invalid value of the angular velocity). Distances
404  // are signed (they are taken along the X axis)
405  const real distLeftWheel = -C.x;
406  const real distRightWheel = -(C.x - A.x); // A.x is equal to D.x
407  const real angularVelocity = (fabs(distLeftWheel) > fabs(distRightWheel)) ? (-leftSpeed / distLeftWheel) : (-rightSpeed / distRightWheel);
408 
409  // The angular displacement is simply the angular velocity multiplied by the timeStep. Here we
410  // also compute the center of rotation in world coordinates (we also need to compute it in the frame
411  // of reference centered between the wheels, not in O) and the axis of rotation (that is simply the
412  // z axis of the robot in world coordinates)
413  const real angularDisplacement = angularVelocity * world()->timeStep();
414  const wVector centerOfRotation = matrix().transformVector(C - wVector((wheelstm[0].w_pos.x - wheelstm[1].w_pos.x) / 2.0, 0.0, 0.0));
415  const wVector axisOfRotation = matrix().z_ax;
416 
417  // Rotating the robot transformation matrix to obtain the new position
418  mtr = mtr.rotateAround(axisOfRotation, centerOfRotation, angularDisplacement);
419  }
420 
421  // This will also update the position of all pieces
422  setMatrix(mtr);
423  }
424 }
425 
427 {
428  // Updating sensors
429  if (proximityIR->isEnabled()) {
430  proximityIR->update();
431  }
432  if (groundBottomIR->isEnabled()) {
433  groundBottomIR->update();
434  }
435  if (groundAroundIR->isEnabled()) {
436  groundAroundIR->update();
437  }
438 
439  // Updating the transformation matrix of the robot. It is coincident with the matrix of the base
440  tm = base->matrix();
441 }
442 
443 void PhyMarXbot::setProximityIRSensorsGraphicalProperties(bool drawSensor, bool drawRay, bool drawRealRay)
444 {
445  proximityIR->setGraphicalProperties(drawSensor, drawRay, drawRealRay);
446 }
447 
448 void PhyMarXbot::setGroundBottomIRSensorsGraphicalProperties(bool drawSensor, bool drawRay, bool drawRealRay)
449 {
450  groundBottomIR->setGraphicalProperties(drawSensor, drawRay, drawRealRay);
451 }
452 
453 void PhyMarXbot::setGroundAroundIRSensorsGraphicalProperties(bool drawSensor, bool drawRay, bool drawRealRay)
454 {
455  groundAroundIR->setGraphicalProperties(drawSensor, drawRay, drawRealRay);
456 }
457 
459 {
460  if (kinematicSimulation == k) {
461  return;
462  }
463 
464  kinematicSimulation = k;
465  if (kinematicSimulation) {
466  // First disabling all joints...
467  for (int i = 0; i < wheelJoints.size(); i++) {
468  wheelJoints[i]->enable(false);
469  }
470  forcesensor->enable(false);
471 
472  // ... then setting all objects to kinematic behaviour
473  base->setKinematic(true);
474  for (int i = 0; i < wheels.size(); i++) {
475  wheels[i]->setKinematic(true);
476  }
477  attachring->setKinematic(true);
478  } else {
479  // First setting all objects to dynamic behaviour...
480  base->setKinematic(false);
481  for (int i = 0; i < wheels.size(); i++) {
482  wheels[i]->setKinematic(false);
483  }
484  attachring->setKinematic(false);
485 
486  // ... then enabling all joints (if the corresponding part is enabled)
487  for (int i = 0; i < wheelJoints.size(); i++) {
488  wheelJoints[i]->enable(true);
489  wheelJoints[i]->updateJointInfo();
490  }
491  forcesensor->enable(true);
492  forcesensor->updateJointInfo();
493  }
494 }
495 
497 {
498  leftWheelVelocity = velocity;
499 }
500 
502 {
503  rightWheelVelocity = velocity;
504 }
505 
507  wMatrix tm = matrix();
508  base->setMatrix( tm );
509  for( int i=0; i<wheels.size(); i++ ) {
510  wheels[i]->setMatrix( wheelstm[i] * tm );
511  }
512  foreach( PhyJoint* ajoint, wheelJoints ) {
513  ajoint->updateJointInfo();
514  }
515  attachring->setMatrix( attachringtm * tm );
516  forcesensor->updateJointInfo();
517 }
518 
519 } // end namespace farsa