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