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