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