phyepuck.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 "phyepuck.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 "graphicalmarkers.h"
33 #include <cmath>
34 
35 namespace farsa {
36 
37 // All measures have been taken on the LARAL e-puck or the official specifications. Lengths are in
38 // meters, weights in kilograms.
39 const real PhyEpuck::batteryplacex = 0.043f;
40 const real PhyEpuck::batteryplacey = 0.055f;
41 const real PhyEpuck::batteryplacez = 0.033f;
42 const real PhyEpuck::batterym = 0.042f;
43 const real PhyEpuck::batteryplacedistancefromground = 0.0015f;
44 const real PhyEpuck::bodyr = 0.035f;
45 const real PhyEpuck::bodyh = 0.021f;
46 const real PhyEpuck::wholebodym = 0.078f;
47 const real PhyEpuck::wheelr = 0.0205f;
48 const real PhyEpuck::wheelh = 0.003f;
49 const real PhyEpuck::wheelm = 0.010f;
50 const real PhyEpuck::axletrack = 0.053f;
51 const real PhyEpuck::passivewheelr = 0.005f;
52 const real PhyEpuck::passivewheelm = 0.005f;
53 
54 PhyEpuck::PhyEpuck(World* world, QString name, const wMatrix& transformation) :
55  WObject(world, name, transformation, false),
56  m_body(NULL),
57  m_wheels(),
58  m_wheelsTransformation(),
59  m_wheelJoints(),
60  m_wheelsCtrl(NULL),
61  m_proximityIR(NULL),
62  m_groundIR(NULL),
63  m_kinematicSimulation(false),
64  m_leftWheelVelocity(0.0f),
65  m_rightWheelVelocity(0.0f),
66  m_frontMarker(NULL)
67 {
68  // --- reference frame
69  // X
70  // ^
71  // | ------
72  // | | |
73  // | -------------------
74  // | | battery |
75  // | | |
76  // | -------------------
77  // | | |
78  // | ------
79  // |
80  // +---------------------> Y
81  // The front of the robot is towards -Y (positive speeds of the wheel make the robot move towards -Y)
82  // and the camera is in -Y
83 
84  // Creating a material for the e-puck
85  world->materials().createMaterial("epuckMaterial");
86  world->materials().setProperties("epuckMaterial", "default", 0.0f, 0.0f, 0.01f, 0.01f, true);
87  world->materials().createMaterial("epuckTire");
88  world->materials().setProperties("epuckTire", "default", 1.2f, 0.9f, 0.01f, 0.01f, true);
89 
90  // Now creating the body of the e-puck. It is made up of two pieces: a box modelling the battery
91  // pack and a short cylinder modelling the upper part (where the electronic board is). We put the
92  // frame of reference of the body (which is equal to that of the whole robot) on the ground to have
93  // move stability and to simplify moving the robot (no need to apply displacements to put it on a
94  // plane)
95  {
96  QVector<PhyObject*> bodyObjs;
98  // First we create the battery pack
100  tm.w_pos.z = (batteryplacez / 2.0f) + batteryplacedistancefromground;
101  obj->setMatrix(tm);
102  bodyObjs << obj;
103  // Then we create the upper part
104  obj = new PhyCylinder(bodyr, bodyh, world);
105  tm = wMatrix::yaw(toRad(90.0f));
106  tm.w_pos.z = batteryplacez + batteryplacedistancefromground + (bodyh / 2.0f);
107  obj->setMatrix( tm );
108  bodyObjs << obj;
109  // Finally we create the compound object actually modelling the body
110  m_body = new PhyCompoundObject(bodyObjs, world, "base");
111  m_body->setMass(batterym + wholebodym);
112  m_body->setMaterial("epuckMaterial");
113  m_body->setOwner(this, false);
114  m_body->setMatrix(transformation);
115  }
116 
117  // Creating all the wheels. We first create the two motorized wheels and then the two passive ones.
118  // The position of the wheels and their indexes are as follows (remember that the front of the
119  // robot is towards -Y):
120  //
121  // X
122  // ^
123  // | ------
124  // | | 0 |
125  // | -------------------
126  // | | battery |
127  // | | 3 2 |
128  // | -------------------
129  // | | 1 |
130  // | ------
131  // |
132  // +---------------------> Y
133 
134  // Creating the first motorized wheel and its joint
135  {
136  // The matrix is relative to the base. First creating the wheel
138  tm.w_pos = wVector(axletrack / 2.0f, 0.0f, wheelr);
139  PhyObject* wheel = new PhyCylinder(wheelr, wheelh, world, "motorWheel");
140  wheel->setMass(wheelm);
141  wheel->setColor(Qt::blue);
142  wheel->setMaterial("epuckTire");
143  wheel->setOwner(this, false);
144  wheel->setMatrix(tm * m_body->matrix());
145  m_wheels.append(wheel);
146  m_wheelsTransformation.append(tm);
147 
148  // Now creating the joint
149  PhyJoint* joint = new PhySuspension(m_body->matrix().x_ax, wheel->matrix().w_pos, m_body->matrix().z_ax, m_body, wheel);
150  joint->dofs()[0]->disableLimits();
151  joint->setOwner(this, false);
152  joint->updateJointInfo();
153  m_wheelJoints.append(joint);
154  }
155 
156  // Creating the second motorized wheel and its joint
157  {
158  // The matrix is relative to the base. First creating the wheel
160  tm.w_pos = wVector(-axletrack / 2.0f, 0.0f, wheelr);
161  PhyObject* wheel = new PhyCylinder(wheelr, wheelh, world, "motorWheel");
162  wheel->setMass(wheelm);
163  wheel->setColor(Qt::blue);
164  wheel->setMaterial("epuckTire");
165  wheel->setOwner(this, false);
166  wheel->setMatrix(tm * m_body->matrix());
167  m_wheels.append(wheel);
168  m_wheelsTransformation.append(tm);
169 
170  // Now creating the joint
171  PhyJoint* joint = new PhySuspension(m_body->matrix().x_ax, wheel->matrix().w_pos, m_body->matrix().z_ax, m_body, wheel);
172  joint->dofs()[0]->disableLimits();
173  joint->setOwner(this, false);
174  joint->updateJointInfo();
175  m_wheelJoints.append(joint);
176  }
177 
178  // Creating the first passive wheel and its joint
179  {
180  // The matrix is relative to the base. First creating the wheel
182  tm.w_pos = wVector(0.0f, (batteryplacey / 2.0f) - passivewheelr, passivewheelr);
183  PhyObject* wheel = new PhySphere(passivewheelr, world, "passiveWheel");
184  wheel->setMass(passivewheelm);
185  wheel->setColor(Qt::blue);
186  wheel->setMaterial("epuckTire");
187  wheel->setOwner(this, false);
188  wheel->setMatrix(tm * m_body->matrix());
189  m_wheels.append(wheel);
190  m_wheelsTransformation.append(tm);
191 
192  // Now creating the joint
193  PhyJoint* joint = new PhyBallAndSocket(wheel->matrix().w_pos, m_body, wheel);
194  joint->setOwner(this, false);
195  joint->updateJointInfo();
196  m_wheelJoints.append(joint);
197  }
198 
199  // Creating the second passive wheel and its joint
200  {
201  // The matrix is relative to the base. First creating the wheel
203  tm.w_pos = wVector(0.0f, -((batteryplacey / 2.0f) - passivewheelr), passivewheelr);
204  PhyObject* wheel = new PhySphere(passivewheelr, world, "passiveWheel");
205  wheel->setMass(passivewheelm);
206  wheel->setColor(Qt::blue);
207  wheel->setMaterial("epuckTire");
208  wheel->setOwner(this, false);
209  wheel->setMatrix(tm * m_body->matrix());
210  m_wheels.append(wheel);
211  m_wheelsTransformation.append(tm);
212 
213  // Now creating the joint
214  PhyJoint* joint = new PhyBallAndSocket(wheel->matrix().w_pos, m_body, wheel);
215  joint->setOwner(this, false);
216  joint->updateJointInfo();
217  m_wheelJoints.append(joint);
218  }
219 
220  // Now we can create the motor controller
221  QVector<PhyDOF*> motors;
222  motors << m_wheelJoints[0]->dofs()[0] << m_wheelJoints[1]->dofs()[0];
223  m_wheelsCtrl = new WheelMotorController(motors, world);
224  m_wheelsCtrl->setOwner(this, false);
225  // The min and max speed in rad/s have been derived for the e-puck wheel considering as maximum linear
226  // displacement 14 cm/s (that for the give wheel radius is approximately 1.1 ratations per second)
227  const real maxAngularSpeed = 1.1f * 2.0f * PI_GRECO;
228  m_wheelsCtrl->setSpeedLimits(-maxAngularSpeed, -maxAngularSpeed, maxAngularSpeed, maxAngularSpeed);
229 
230  // Connecting wheels speed signals to be able to move the robot when in kinematic
231  connect(m_wheelJoints[0]->dofs()[0], SIGNAL(changedDesiredVelocity(real)), this, SLOT(setRightWheelDesideredVelocity(real)));
232  connect(m_wheelJoints[1]->dofs()[0], SIGNAL(changedDesiredVelocity(real)), this, SLOT(setLeftWheelDesideredVelocity(real)));
233 
234  // Creating the proximity IR sensors
235  QVector<SingleIR> sensors;
236 
237 #ifdef __GNUC__
238  #warning QUI DECIDERE PER I RANGE/APERTURE E LE POSIZIONI
239 #endif
240  // Adding the sensors. The epuck has 8 proximity infrared sensors
241  for (unsigned int i = 0; i < 8; i++) {
242  const double curAngle = double(i) * (360.0 / 8.0) + ((360.0 / 8.0) / 2.0);
243  const double radius = bodyr;
244 
245  wMatrix mtr = wMatrix::yaw(deg2rad(curAngle + 90.0)) * wMatrix::pitch(deg2rad(90.0f));
246  mtr.w_pos.x = radius * cos(deg2rad(curAngle));
247  mtr.w_pos.y = radius * sin(deg2rad(curAngle));
249 
250  sensors.append(SingleIR(m_body, mtr, 0.01, 0.05, 10.0, 5));
251  }
252  m_proximityIR = new SimulatedIRProximitySensorController(world, sensors);
253 
254  // Now creating the three ground IR sensors
255  sensors.clear();
256  wMatrix mtr = wMatrix::yaw(deg2rad(180.0));
257 
258  // Adding the sensors. The e-puck has 3 ground infrared sensors in the front part
259  mtr.w_pos = wVector(-batteryplacex / 2.0f, -batteryplacey / 2.0f, batteryplacedistancefromground);
260  sensors.append(SingleIR(m_body, mtr, 0.0, mtr.w_pos.z + 0.005, 0.0, 1));
261  mtr.w_pos = wVector(0.0f, -batteryplacey / 2.0f, batteryplacedistancefromground);
262  sensors.append(SingleIR(m_body, mtr, 0.0, mtr.w_pos.z + 0.005, 0.0, 1));
264  sensors.append(SingleIR(m_body, mtr, 0.0, mtr.w_pos.z + 0.005, 0.0, 1));
265  m_groundIR = new SimulatedIRGroundSensorController(world, sensors);
266 
267  world->pushObject(this);
268 }
269 
271 {
272  foreach (PhyJoint* j, m_wheelJoints) {
273  delete j;
274  }
275  foreach (PhyObject* w, m_wheels) {
276  delete w;
277  }
278  delete m_wheelsCtrl;
279  delete m_body;
280  delete m_proximityIR;
281  delete m_groundIR;
282 }
283 
285 {
286  // Updating motors
287  if (m_wheelsCtrl->isEnabled()) {
288  m_wheelsCtrl->update();
289  }
290 
291  if (m_kinematicSimulation) {
292  // In kinematic mode, we have to manually move the robot depending on the wheel velocities
293  wMatrix mtr = matrix();
294  wheeledRobotsComputeKinematicMovement(mtr, m_leftWheelVelocity, m_rightWheelVelocity, wheelr, axletrack, world()->timeStep());
295 
296  // This will also update the position of all pieces
297  setMatrix(mtr);
298  }
299 }
300 
302 {
303  // Updating sensors
304  if (m_proximityIR->isEnabled()) {
305  m_proximityIR->update();
306  }
307  if (m_groundIR->isEnabled()) {
308  m_groundIR->update();
309  }
310 
311  // Updating the transformation matrix of the robot. It is coincident with the matrix of the body
312  tm = m_body->matrix();
313 }
314 
315 void PhyEpuck::setProximityIRSensorsGraphicalProperties(bool drawSensor, bool drawRay, bool drawRealRay)
316 {
317  m_proximityIR->setGraphicalProperties(drawSensor, drawRay, drawRealRay);
318 }
319 
320 void PhyEpuck::setGroundIRSensorsGraphicalProperties(bool drawSensor, bool drawRay, bool drawRealRay)
321 {
322  m_groundIR->setGraphicalProperties(drawSensor, drawRay, drawRealRay);
323 }
324 
325 void PhyEpuck::setDrawFrontMarker(bool drawMarker)
326 {
327  if (getDrawFrontMarker() == drawMarker) {
328  return;
329  }
330 
331  if (drawMarker) {
332  m_frontMarker = new PlanarArrowGraphicalMarker(bodyr, bodyr / 6.0f, bodyr / 4.0f, 0.7f, world());
333  m_frontMarker->setUseColorTextureOfOwner(false);
334  m_frontMarker->setColor(Qt::green);
335  m_frontMarker->setTexture("");
336 
337  wMatrix displacement = wMatrix::roll(-PI_GRECO / 2.0f);
338  displacement.w_pos = wVector(0.0, 0.0, batteryplacedistancefromground + batteryplacez + bodyh + 0.0001f);
339  m_frontMarker->attachToObject(this, true, displacement);
340  } else {
341  delete m_frontMarker;
342  m_frontMarker = NULL;
343  }
344 }
345 
347 {
348  return (m_frontMarker != NULL);
349 }
350 
352 {
353  if (m_kinematicSimulation == k) {
354  return;
355  }
356 
357  m_kinematicSimulation = k;
358  if (m_kinematicSimulation) {
359  // First disabling all joints...
360  for (int i = 0; i < m_wheelJoints.size(); i++) {
361  m_wheelJoints[i]->enable(false);
362  }
363 
364  // ... then setting all objects to kinematic behaviour
365  m_body->setKinematic(true, true);
366  for (int i = 0; i < m_wheels.size(); i++) {
367  m_wheels[i]->setKinematic(true, true);
368  }
369  } else {
370  // First setting all objects to dynamic behaviour...
371  m_body->setKinematic(false);
372  for (int i = 0; i < m_wheels.size(); i++) {
373  m_wheels[i]->setKinematic(false);
374  }
375 
376  // ... then enabling all joints (if the corresponding part is enabled)
377  for (int i = 0; i < m_wheelJoints.size(); i++) {
378  m_wheelJoints[i]->enable(true);
379  m_wheelJoints[i]->updateJointInfo();
380  }
381  }
382 }
383 
385 {
386  m_leftWheelVelocity = velocity;
387 }
388 
390 {
391  m_rightWheelVelocity = velocity;
392 }
393 
395 {
396  wMatrix tm = matrix();
397  m_body->setMatrix(tm);
398  for (int i = 0; i < m_wheels.size(); i++) {
399  m_wheels[i]->setMatrix(m_wheelsTransformation[i] * tm);
400  }
401  foreach (PhyJoint* j, m_wheelJoints) {
402  j->updateJointInfo();
403  }
404 }
405 
406 } // end namespace farsa