marxbotsensors.cpp
1 /********************************************************************************
2  * FARSA Experiments Library *
3  * Copyright (C) 2007-2012 *
4  * Gianluca Massera <emmegian@yahoo.it> *
5  * Stefano Nolfi <stefano.nolfi@istc.cnr.it> *
6  * Tomassino Ferrauto <tomassino.ferrauto@istc.cnr.it> *
7  * Onofrio Gigliotta <onofrio.gigliotta@istc.cnr.it> *
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License *
20  * along with this program; if not, write to the Free Software *
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
22  ********************************************************************************/
23 
24 #include "marxbotsensors.h"
25 #include "configurationhelper.h"
26 #include "logger.h"
27 #include "graphicalwobject.h"
28 #include "arena.h"
29 #include <QMutex>
30 
31 namespace farsa {
32 
34  Sensor(params, prefix),
35  m_marxbotResource("robot"),
36  m_neuronsIteratorResource("neuronsIterator")
37 {
38  // Reading parameters
41 
42  // Declaring the resources that are needed here
44 }
45 
47 {
48  // Nothing to do here
49 }
50 
51 void MarXbotSensor::save(ConfigurationParameters& params, QString prefix)
52 {
53  // Calling parent function
54  Sensor::save(params, prefix);
55 
56  // Saving parameters
57  params.startObjectParameters(prefix, "MarXbotSensor", this);
58  params.createParameter(prefix, "marxbot", m_marxbotResource);
59  params.createParameter(prefix, "neuronsIterator", m_neuronsIteratorResource);
60 }
61 
62 void MarXbotSensor::describe(QString type)
63 {
64  // Calling parent function
65  Sensor::describe(type);
66 
67  // Describing our parameters
68  Descriptor d = addTypeDescription(type, "The base class for MarXbot sensors");
69  d.describeString("marxbot").def("robot").help("The name of the resource associated with the MarXbot robot to use (default is \"robot\")");
70  d.describeString("neuronsIterator").def("neuronsIterator").help("The name of the resource associated with the neural network iterator (default is \"neuronsIterator\")");
71 }
72 
73 void MarXbotSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
74 {
75  // Calling parent function
76  Sensor::resourceChanged(resourceName, changeType);
77 
78  // Here we only check whether the resource has been deleted and reset the check flag, the
79  // actual work is done in subclasses
80  if (changeType == Deleted) {
82  return;
83  }
84 }
85 
87  MarXbotSensor(params, prefix),
88  m_robot(NULL),
89  m_neuronsIterator(NULL),
90  m_activeSensors(ConfigurationHelper::getBoolVector(params, prefix + "activeSensors", "111111111111111111111111")),
91  m_numActiveSensors(m_activeSensors.count(true))
92 {
93  if (m_activeSensors.size() != 24) {
94  ConfigurationHelper::throwUserConfigError(prefix + "activeSensors", params.getValue(prefix + "activeSensors"), "The parameter must be a list of exactly 24 elements either equal to 1 or to 0 (do not use any space to separate elements, just put them directly one after the other)");
95  }
96 }
97 
99 {
100  // Nothing to do here
101 }
102 
104 {
105  // Calling parent function
106  MarXbotSensor::save(params, prefix);
107 
108  // Saving parameters
109  params.startObjectParameters(prefix, "MarXbotProximityIRSensor", this);
110  QString activeSensorsString;
111  for (int i = 0; i < m_activeSensors.size(); i++) {
112  activeSensorsString += (m_activeSensors[i] ? "1" : "0");
113  }
114  params.createParameter(prefix, "activeSensors", activeSensorsString);
115 }
116 
118 {
119  // Calling parent function
121 
122  // Describing our parameters
123  Descriptor d = addTypeDescription(type, "The infrared proximity sensors of the MarXbot robot", "The infrared proximity sensors of the MarXbot robot. These are the very short range IR sensors all around the base");
124  d.describeString("activeSensors").def("111111111111111111111111").help("Which IR sensors of the robot are actually enabled", "This is a string of exactly 24 elements. Each element can be either \"0\" or \"1\" to respectively disable/enable the corresponding proximity IR sensor. The first sensor is the one on the left side of the robot and the others follow counterclockwise (i.e. left, back, right, front)");
125 }
126 
128 {
129  // Checking all resources we need exist
131 
132  // Acquiring the lock to get resources
133  ResourcesLocker locker( this );
134 
135  m_neuronsIterator->setCurrentBlock(name());
136  for (int i = 0; i < m_activeSensors.size(); i++) {
137  if (m_activeSensors[i]) {
138  m_neuronsIterator->setInput(m_robot->proximityIRSensorController()->activation(i));
139  m_neuronsIterator->nextNeuron();
140  }
141  }
142 }
143 
145 {
146  return m_numActiveSensors;
147 }
148 
149 void MarXbotProximityIRSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
150 {
151  // Calling parent function
152  MarXbotSensor::resourceChanged(resourceName, changeType);
153 
154  if (changeType == Deleted) {
155  return;
156  }
157 
158  if (resourceName == m_marxbotResource) {
159  m_robot = getResource<PhyMarXbot>();
160 
161  // Eanbling sensors
162  m_robot->proximityIRSensorController()->setEnabled(true);
163 
164  // Now enabling/disabling individual sensors depending on the m_activeSensors vector
165  for (int i = 0; i < m_activeSensors.size(); i++) {
166  m_robot->proximityIRSensorController()->setSensorActive(i, m_activeSensors[i]);
167  }
168  } else if (resourceName == m_neuronsIteratorResource) {
169  m_neuronsIterator = getResource<NeuronsIterator>();
170  m_neuronsIterator->setCurrentBlock(name());
171 
172  // We use the actual sensor ID in the neuron name
173  for (int i = 0; i < m_activeSensors.size(); i++) {
174  if (m_activeSensors[i]) {
175  m_neuronsIterator->setGraphicProperties("ir" + QString::number(i), 0.0, 1.0, Qt::red);
176  m_neuronsIterator->nextNeuron();
177  }
178  }
179  } else {
180  Logger::info("Unknown resource " + resourceName + " for " + name());
181  }
182 }
183 
185  MarXbotSensor(params, prefix),
186  m_robot(NULL),
187  m_neuronsIterator(NULL)
188 {
189 }
190 
192 {
193  // Nothing to do here
194 }
195 
197 {
198  // Calling parent function
199  MarXbotSensor::save(params, prefix);
200 
201  // Saving parameters
202  params.startObjectParameters(prefix, "MarXbotGroundBottomIRSensor", this);
203 }
204 
206 {
207  // Calling parent function
209 
210  // Describing our parameters
211  Descriptor d = addTypeDescription(type, "The infrared ground bottom sensors of the MarXbot robot", "The infrared ground bottom sensors of the MarXbot robot. These are the four ground sensors below the robot battery pack.");
212 }
213 
215 {
216  // Checking all resources we need exist
218 
219  // Acquiring the lock to get resources
220  ResourcesLocker locker( this );
221 
222  m_neuronsIterator->setCurrentBlock(name());
223  for (int i = 0; i < size(); i++, m_neuronsIterator->nextNeuron()) {
224  m_neuronsIterator->setInput(m_robot->groundBottomIRSensorController()->activation(i));
225  }
226 }
227 
229 {
230  return 4;
231 }
232 
233 void MarXbotGroundBottomIRSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
234 {
235  // Calling parent function
236  MarXbotSensor::resourceChanged(resourceName, changeType);
237 
238  if (changeType == Deleted) {
239  return;
240  }
241 
242  if (resourceName == m_marxbotResource) {
243  m_robot = getResource<PhyMarXbot>();
244 
245  // Eanbling sensors
246  m_robot->groundBottomIRSensorController()->setEnabled(true);
247  } else if (resourceName == m_neuronsIteratorResource) {
248  m_neuronsIterator = getResource<NeuronsIterator>();
249  m_neuronsIterator->setCurrentBlock(name());
250  for (int i = 0; i < size(); i++, m_neuronsIterator->nextNeuron()) {
251  m_neuronsIterator->setGraphicProperties("gb" + QString::number(i), 0.0, 1.0, Qt::red);
252  }
253  } else {
254  Logger::info("Unknown resource " + resourceName + " for " + name());
255  }
256 }
257 
259  MarXbotSensor(params, prefix),
260  m_robot(NULL),
261  m_neuronsIterator(NULL)
262 {
263 }
264 
266 {
267  // Nothing to do here
268 }
269 
271 {
272  // Calling parent function
273  MarXbotSensor::save(params, prefix);
274 
275  // Saving parameters
276  params.startObjectParameters(prefix, "MarXbotGroundAroundIRSensor", this);
277 }
278 
280 {
281  // Calling parent function
283 
284  // Describing our parameters
285  Descriptor d = addTypeDescription(type, "The infrared ground around sensors of the MarXbot robot", "The infrared ground around sensors of the MarXbot robot. These are the eight ground sensors below the base of the robot (just above the wheels).");
286 }
287 
289 {
290  // Checking all resources we need exist
292 
293  // Acquiring the lock to get resources
294  ResourcesLocker locker( this );
295 
296  m_neuronsIterator->setCurrentBlock(name());
297  for (int i = 0; i < size(); i++, m_neuronsIterator->nextNeuron()) {
298  m_neuronsIterator->setInput(m_robot->groundAroundIRSensorController()->activation(i));
299  }
300 }
301 
303 {
304  return 8;
305 }
306 
307 void MarXbotGroundAroundIRSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
308 {
309  // Calling parent function
310  MarXbotSensor::resourceChanged(resourceName, changeType);
311 
312  if (changeType == Deleted) {
313  return;
314  }
315 
316  if (resourceName == m_marxbotResource) {
317  m_robot = getResource<PhyMarXbot>();
318 
319  // Eanbling sensors
320  m_robot->groundAroundIRSensorController()->setEnabled(true);
321  } else if (resourceName == m_neuronsIteratorResource) {
322  m_neuronsIterator = getResource<NeuronsIterator>();
323  m_neuronsIterator->setCurrentBlock(name());
324  for (int i = 0; i < size(); i++, m_neuronsIterator->nextNeuron()) {
325  m_neuronsIterator->setGraphicProperties("ga" + QString::number(i), 0.0, 1.0, Qt::red);
326  }
327  } else {
328  Logger::info("Unknown resource " + resourceName + " for " + name());
329  }
330 }
331 
333  MarXbotSensor(params, prefix),
334  m_robot(NULL),
335  m_neuronsIterator(NULL),
336  m_numReceptors(ConfigurationHelper::getInt(params, prefix + "numReceptors", 8)),
337  m_useRedChannel(ConfigurationHelper::getBool(params, prefix + "useRedChannel", true)),
338  m_useGreenChannel(ConfigurationHelper::getBool(params, prefix + "useGreenChannel", true)),
339  m_useBlueChannel(ConfigurationHelper::getBool(params, prefix + "useBlueChannel", true)),
340  m_usedColorChannels((m_useRedChannel ? 1 : 0) + (m_useGreenChannel ? 1 : 0) + (m_useBlueChannel ? 1 : 0)),
341  m_aperture(ConfigurationHelper::getDouble(params, prefix + "aperture", 360.0f)),
342  m_camera(NULL),
343  m_drawCamera(ConfigurationHelper::getBool(params, prefix + "drawCamera", true))
344 {
345  // Few sanity checks
346  if (m_numReceptors <= 0) {
347  ConfigurationHelper::throwUserConfigError(prefix + "numReceptors", params.getValue(prefix + "numReceptors"), "The parameter must be an integer greater or equal to 1");
348  }
349  if ((m_aperture < 0.0f) || (m_aperture > 360.0f)) {
350  ConfigurationHelper::throwUserConfigError(prefix + "aperture", params.getValue(prefix + "aperture"), "The parameter must be a real number between 0 and 360");
351  }
352 }
353 
355 {
356  // Deleting the camera
357  delete m_camera;
358 }
359 
361 {
362  // Calling parent function
363  MarXbotSensor::save(params, prefix);
364 
365  // Saving parameters
366  params.startObjectParameters(prefix, "MarXbotLinearCameraSensor", this);
367  params.createParameter(prefix, "numReceptors", QString::number(m_numReceptors));
368  params.createParameter(prefix, "useRedChannel", (m_useRedChannel ? "true" : "false"));
369  params.createParameter(prefix, "useGreenChannel", (m_useGreenChannel ? "true" : "false"));
370  params.createParameter(prefix, "useBlueChannel", (m_useBlueChannel ? "true" : "false"));
371  params.createParameter(prefix, "aperture", QString::number(m_aperture));
372  params.createParameter(prefix, "drawCamera", (m_drawCamera ? "true" : "false"));
373 }
374 
376 {
377  // Calling parent function
379 
380  // Describing our parameters
381  Descriptor d = addTypeDescription(type, "The linear camera sensor of the MarXbot robot", "This is a 360° linear camera");
382  d.describeInt("numReceptors").def(8).limits(1, MaxInteger).help("The number of receptors of the sensor", "Each receptor returns three values, one for each of the three colors (red, green, blue). This means that the size returned by this sensor is 3 * numReceptors (default is 8)");
383  d.describeBool("useRedChannel").def(true).help("Whether the red component of the perceived objects should be used or not (default true)");
384  d.describeBool("useGreenChannel").def(true).help("Whether the green component of the perceived objects should be used or not (default true)");
385  d.describeBool("useBlueChannel").def(true).help("Whether the blue component of the perceived objects should be used or not (default true)");
386  d.describeReal("aperture").def(360.0f).limits(0.0f, 360.0f).help("The aperture of the camera in degrees", "The real MarXbot has an omnidirectional camera, so you can use here any value up to 360 degrees (default is 360)");
387  d.describeBool("drawCamera").def(true).help("Whether to draw the camera or not");
388 }
389 
391 {
392  // Checking all resources we need exist
394 
395  // Acquiring the lock to get resources
396  ResourcesLocker locker(this);
397 
398  // Updating the camera
399  m_camera->update();
400 
401  // Reading activations: first the red one, then the green one and finally the blue one
402  m_neuronsIterator->setCurrentBlock(name());
403  if (m_useRedChannel) {
404  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
405  m_neuronsIterator->setInput(m_camera->colorForReceptor(i).redF());
406  }
407  }
408  if (m_useGreenChannel) {
409  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
410  m_neuronsIterator->setInput(m_camera->colorForReceptor(i).greenF());
411  }
412  }
413  if (m_useBlueChannel) {
414  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
415  m_neuronsIterator->setInput(m_camera->colorForReceptor(i).blueF());
416  }
417  }
418 }
419 
421 {
422  return m_numReceptors * m_usedColorChannels;
423 }
424 
425 void MarXbotLinearCameraSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
426 {
427  // Calling parent function
428  MarXbotSensor::resourceChanged(resourceName, changeType);
429 
430  if (changeType == Deleted) {
431  // Deleting the camera if the robot was deleted
432  if (resourceName == m_marxbotResource) {
433  delete m_camera;
434  m_camera = NULL;
435  }
436 
437  return;
438  }
439 
440  if (resourceName == m_marxbotResource) {
441  m_robot = getResource<PhyMarXbot>();
442 
443  // Now we can also create the camera
444  wMatrix mtr = wMatrix::roll(-PI_GRECO / 2.0);
445 #ifdef __GNUC__
446  #warning QUI INVECE DI UNA COSTANTE, CALCOLARSI UNA POSIZIONE DALLE DIMENSIONI DEL ROBOT
447 #endif
448  mtr.w_pos.z = 0.13f;
449  m_camera = new LinearCamera(m_robot, mtr, toRad(m_aperture), m_numReceptors, Qt::black);
450 
451  // Sharing resources with the camera
452  m_camera->shareResourcesWith(this);
453  m_camera->drawCamera(m_drawCamera);
454  } else if (resourceName == m_neuronsIteratorResource) {
455  m_neuronsIterator = getResource<NeuronsIterator>();
456  m_neuronsIterator->setCurrentBlock(name());
457  if (m_useRedChannel) {
458  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
459  m_neuronsIterator->setGraphicProperties("lr" + QString::number(i), 0.0, 1.0, Qt::red);
460  }
461  }
462  if (m_useGreenChannel) {
463  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
464  m_neuronsIterator->setGraphicProperties("lg" + QString::number(i), 0.0, 1.0, Qt::red);
465  }
466  }
467  if (m_useBlueChannel) {
468  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
469  m_neuronsIterator->setGraphicProperties("lb" + QString::number(i), 0.0, 1.0, Qt::red);
470  }
471  }
472  } else {
473  Logger::info("Unknown resource " + resourceName + " for " + name());
474  }
475 }
476 
477 namespace __MarXbotTractionSensor_internal {
478  #ifndef GLMultMatrix
479  #define GLMultMatrix glMultMatrixf
480  // for double glMultMatrixd
481  #endif
482 
490  {
491  public:
512  TractionSensorGraphic(WObject *object, const wVector& offset, real radius, real scale = 1.0, real maxLength = 1.0, QString name = "unamed") :
513  GraphicalWObject(object->world(), name),
514  m_object(object),
515  m_radius(radius),
516  m_scale(scale),
517  m_maxLength(maxLength),
518  m_vector(0.0, 0.0, 0.0),
519  m_vectorMutex()
520  {
521  // Attaching to object (which also becomes our owner). We also build the displacement matrix
522  wMatrix displacement = wMatrix::identity();
523  displacement.w_pos = offset;
524  attachToObject(m_object, true, displacement);
525 
526  // We also use our own color and texture
528  setTexture("");
529  }
530 
535  {
536  }
537 
546  void setVector(const wVector vector)
547  {
548  m_vectorMutex.lock();
549  m_vector = vector;
550  m_vectorMutex.unlock();
551  }
552 
553  protected:
562  virtual void render(RenderWObject* renderer, QGLContext* gw)
563  {
564  // Copying the m_activations vector to a local vector to avoid concurrent accesses.
565  m_vectorMutex.lock();
566  const wVector vector = m_vector;
567  m_vectorMutex.unlock();
568 
569  // Setting the matrix to draw the vector.
570  glPushMatrix();
571  GLMultMatrix(&tm[0][0]);
572 
573  // Before drawing computing the length of the body and of the tip of the arrow. The
574  // tip is always m_radius long, unless the arrow is so small that no body is present.
575  // The bodyLength will be negative if the vector is very short: in this cas we won't
576  // draw the arrow body. The radius of the tip is 3/2 of the cylinder radius unless the
577  // arrow is very small (and has no body): in this case the radius is scaled accordingly.
578  // We also have to take into account the maximum allowed length of the vector and
579  // change color if it is too long
580  const real originalVectorLength = vector.norm();
581  const wVector axis = vector.scale(1.0 / originalVectorLength);
582  const QColor vectorColor = (originalVectorLength > m_maxLength) ? Qt::red : Qt::cyan;
583  const real vectorLength = min(originalVectorLength, m_maxLength) * m_scale;
584  const real tipLength = min(vectorLength, m_radius);
585  const real tipRadius = ((m_radius * 3.0) / 2.0) * (tipLength / m_radius);
586  const real bodyLength = vectorLength - m_radius;
587 
588  // Setting the color of the vector
589  setColor(vectorColor);
590  renderer->container()->setupColorTexture(gw, renderer);
591 
592  // First drawing the body if its length is positive
593  if (bodyLength > 0.0) {
594  RenderWObjectContainer::drawCylinder(axis, axis.scale(0.5 * bodyLength), bodyLength, m_radius, color());
595  }
596 
597  // Now drawing the tip as a cone:
598  {
599  GLUquadricObj *pObj;
600 
601  // opengl cylinder are aligned alogn the z axis, we want it along our axis,
602  // we create a rotation matrix to do the alignment. Moreover the cone must
603  // start at the end of the cylinder we draw before
604  const wVector tipDisplacement = (bodyLength < 0.0) ? wVector(0.0, 0.0, 0.0) : axis.scale(bodyLength);
605  glPushMatrix();
607  matrix.w_pos = tipDisplacement;
608  GLMultMatrix(&matrix[0][0]);
609 
610  // Get a new Quadric off the stack
611  pObj = gluNewQuadric();
612  gluQuadricTexture(pObj, true);
613  gluCylinder(pObj, tipRadius, 0, tipLength, 20, 2);
614 
615  // render the caps
616  gluQuadricOrientation(pObj, GLU_INSIDE);
617  gluDisk(pObj, 0.0f, tipRadius, 20, 1);
618 
619  gluDeleteQuadric(pObj);
620  glPopMatrix();
621  }
622 
623  // Popping twice because we pushed both our matrix and the offset matrix
624  glPopMatrix();
625  glPopMatrix();
626  }
627 
632 
637  const real m_radius;
638 
642  const real m_scale;
643 
651  const real m_maxLength;
652 
658  wVector m_vector;
659 
667  };
668 }
669 
670 using namespace __MarXbotTractionSensor_internal;
671 
673  MarXbotSensor(params, prefix),
674  m_robot(NULL),
675  m_neuronsIterator(NULL),
676  m_maxForce(ConfigurationHelper::getDouble(params, prefix + "maxForce", 1.0f)),
677  m_drawSensor(ConfigurationHelper::getBool(params, prefix + "drawSensor", true)),
678  m_graphics(NULL)
679 {
680  if (m_maxForce < 0.0) {
681  ConfigurationHelper::throwUserConfigError(prefix + "maxForce", params.getValue(prefix + "maxForce"), "The parameter must be a positive real number");
682  }
683 }
684 
686 {
687  // Nothing to do here
688 }
689 
691 {
692  // Calling parent function
693  MarXbotSensor::save(params, prefix);
694 
695  // Saving parameters
696  params.startObjectParameters(prefix, "MarXbotTractionSensor", this);
697  params.createParameter(prefix, "maxForce", QString::number(m_maxForce));
698  params.createParameter(prefix, "drawSensor", m_drawSensor ? QString("true") : QString("false"));
699 }
700 
702 {
703  // Calling parent function
705 
706  // Describing our parameters
707  Descriptor d = addTypeDescription(type, "The traction sensors of the MarXbot robot", "The traction sensors of the MarXbot robot. It is placed between the base and the turret. Note that this sensor only works when the robot is in dynamic mode");
708  d.describeReal("maxForce").def(1.0f).limits(0.0f, +Infinity).help("The maximum possible value of the force", "This is the value of the force on one axis over which the corresponding neuron is activated with 1");
709  d.describeBool("drawSensor").def(true).help("Whether to draw the sensor", "If true the sensor is graphically represented by an arrow in the direction of the current traction");
710 }
711 
713 {
714  // Checking all resources we need exist
716 
717  // Acquiring the lock to get resources
718  ResourcesLocker locker( this );
719 
720  m_neuronsIterator->setCurrentBlock(name());
721  const wVector& t = m_robot->tractionSensorController()->traction();
722 
723  // The first two neurons correspond to the x axis, the second two to the y axis
724  if (t.x > 0.0) {
725  const real f = min(t.x, m_maxForce);
726  m_neuronsIterator->setInput(f / m_maxForce);
727  m_neuronsIterator->nextNeuron();
728  m_neuronsIterator->setInput(0.0);
729  m_neuronsIterator->nextNeuron();
730  } else {
731  const real f = max(t.x, -m_maxForce);
732  m_neuronsIterator->setInput(0.0);
733  m_neuronsIterator->nextNeuron();
734  m_neuronsIterator->setInput(-f / m_maxForce);
735  m_neuronsIterator->nextNeuron();
736  }
737 
738  if (t.y > 0.0) {
739  const real f = min(t.y, m_maxForce);
740  m_neuronsIterator->setInput(f / m_maxForce);
741  m_neuronsIterator->nextNeuron();
742  m_neuronsIterator->setInput(0.0);
743  m_neuronsIterator->nextNeuron();
744  } else {
745  const real f = max(t.y, -m_maxForce);
746  m_neuronsIterator->setInput(0.0);
747  m_neuronsIterator->nextNeuron();
748  m_neuronsIterator->setInput(-f / m_maxForce);
749  m_neuronsIterator->nextNeuron();
750  }
751 
752  // Updating graphical representation if we have to
753  if (m_drawSensor) {
754  // We only draw the x and y components of the vector
755  m_graphics->setVector(wVector(t.x, t.y, 0.0));
756  }
757 }
758 
760 {
761  return 4;
762 }
763 
764 void MarXbotTractionSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
765 {
766  // Calling parent function
767  MarXbotSensor::resourceChanged(resourceName, changeType);
768 
769  if (changeType == Deleted) {
770  return;
771  }
772 
773  if (resourceName == m_marxbotResource) {
774  m_robot = getResource<PhyMarXbot>();
775 
776  // Eanbling sensors
777  m_robot->proximityIRSensorController()->setEnabled(true);
778 
779  // If graphics is enabled, creating the graphical object
780  if (m_drawSensor) {
781  const wVector offset(0.0, 0.0, PhyMarXbot::basez + PhyMarXbot::bodyh + 0.1);
782 
783  m_graphics = new TractionSensorGraphic(m_robot, offset, 0.005, 0.1 / m_maxForce, m_maxForce, "Traction sensor");
784  }
785  } else if (resourceName == m_neuronsIteratorResource) {
786  m_neuronsIterator = getResource<NeuronsIterator>();
787  m_neuronsIterator->setCurrentBlock(name());
788  m_neuronsIterator->setGraphicProperties("t+x", 0.0, 1.0, Qt::red);
789  m_neuronsIterator->nextNeuron();
790  m_neuronsIterator->setGraphicProperties("t-x", 0.0, 1.0, Qt::red);
791  m_neuronsIterator->nextNeuron();
792  m_neuronsIterator->setGraphicProperties("t+y", 0.0, 1.0, Qt::red);
793  m_neuronsIterator->nextNeuron();
794  m_neuronsIterator->setGraphicProperties("t-y", 0.0, 1.0, Qt::red);
795  } else {
796  Logger::info("Unknown resource " + resourceName + " for " + name());
797  }
798 }
799 
801  MarXbotSensor(params, prefix),
802  m_robot(NULL),
803  m_arena(NULL),
804  m_neuronsIterator(NULL),
805  m_activeSensors(ConfigurationHelper::getBoolVector(params, prefix + "activeSensors", "111111111111111111111111")),
806  m_numActiveSensors(m_activeSensors.count(true)),
807  m_roundSamples(ConfigurationHelper::getString(params, prefix + "roundSamples", "round.sam")),
808  m_smallSamples(ConfigurationHelper::getString(params, prefix + "smallSamples", "small.sam")),
809  m_wallSamples(ConfigurationHelper::getString(params, prefix + "wallSamples", "wall.sam"))
810 {
811  if (m_activeSensors.size() != 24) {
812  ConfigurationHelper::throwUserConfigError(prefix + "activeSensors", params.getValue(prefix + "activeSensors"), "The parameter must be a list of exactly 24 elements either equal to 1 or to 0 (do not use any space to separate elements, just put them directly one after the other)");
813  }
814 
815  // Checking that the sampled files have the right number of IR sensors
816  if (m_roundSamples.numIR() != 24) {
817  ConfigurationHelper::throwUserConfigError(prefix + "roundSamples", m_roundSamples.filename(), "The file has samples for the wrong number of sensors, expected 24, got " + QString::number(m_roundSamples.numIR()));
818  }
819  if (m_smallSamples.numIR() != 24) {
820  ConfigurationHelper::throwUserConfigError(prefix + "smallSamples", m_smallSamples.filename(), "The file has samples for the wrong number of sensors, expected 24, got " + QString::number(m_smallSamples.numIR()));
821  }
822  if (m_wallSamples.numIR() != 24) {
823  ConfigurationHelper::throwUserConfigError(prefix + "wallSamples", m_wallSamples.filename(), "The file has samples for the wrong number of sensors, expected 8, got " + QString::number(m_wallSamples.numIR()));
824  }
825 
826  // Here we also need the arena to work
827  addUsableResource("arena");
828 }
829 
831 {
832  // Nothing to do here
833 }
834 
836 {
837  // Calling parent function
838  MarXbotSensor::save(params, prefix);
839 
840  // Saving parameters
841  params.startObjectParameters(prefix, "MarXbotSampledProximityIRSensor", this);
842  QString activeSensorsString;
843  for (int i = 0; i < m_activeSensors.size(); i++) {
844  activeSensorsString += (m_activeSensors[i] ? "1" : "0");
845  }
846  params.createParameter(prefix, "activeSensors", activeSensorsString);
847  params.createParameter(prefix, "roundSamples", m_roundSamples.filename());
848  params.createParameter(prefix, "smallSamples", m_smallSamples.filename());
849  params.createParameter(prefix, "wallSamples", m_wallSamples.filename());
850 }
851 
853 {
854  // Calling parent function
856 
857  // Describing our parameters
858  Descriptor d = addTypeDescription(type, "The sampled proximity infrared sensors of the MarXbot", "This is the sampled version of the proximity infrared sensors of the MarXbot. This sensor only works with objects created using the Arena");
859  d.describeString("activeSensors").def("111111111111111111111111").help("Which IR sensors of the robot are actually enabled", "This is a string of exactly 24 elements. Each element can be either \"0\" or \"1\" to respectively disable/enable the corresponding proximity IR sensor. The first sensor is the one on the left side of the robot and the others follow counterclockwise (i.e. left, back, right, front)");
860  d.describeString("roundSamples").def("round.sam").help("The name of the file with samples for big round objects");
861  d.describeString("smallSamples").def("small.sam").help("The name of the file with samples for small round objects");
862  d.describeString("wallSamples").def("wall.sam").help("The name of the file with samples for walls");
863 }
864 
866 {
867  // Checking all resources we need exist
869 
870  // Acquiring the lock to get resources
871  ResourcesLocker locker(this);
872 
873  // Getting the list of objects in the arena
874  const QVector<PhyObject2DWrapper*>& objectsList = m_arena->getObjects();
875 
876  // Preparing the vector with activations and setting all values to 0
877  QVector<real> activations(m_activeSensors.size(), 0.0);
878 
879  // Cycling through the list of objects. We first need to get the current position and orientation of the robot
880  const wVector robotPos = m_robot->position();
881  const real robotAng = m_robot->orientation(m_arena->getPlane());
882  foreach(const PhyObject2DWrapper* obj, objectsList) {
883  // Computing angle and distance. We don't need to remove the robot to which this sensor belongs because
884  // the calculatations will give a negative distance
885  double distance;
886  double angle;
887 
888  // If computeDistanceAndOrientationFromRobot returns false, we have to discard this object
889  if (!obj->computeDistanceAndOrientationFromRobot(*(m_arena->getRobotWrapper(m_marxbotResource)), distance, angle)) {
890  continue;
891  }
892 
893  // Getting the activation. The switch is to understand which samples to use
894  QVector<unsigned int>::const_iterator actIt = QVector<unsigned int>::const_iterator();
895  switch (obj->type()) {
896  case PhyObject2DWrapper::Wall:
897  actIt = m_wallSamples.getActivation(distance, angle);
898  break;
899  case PhyObject2DWrapper::SmallCylinder:
900  actIt = m_smallSamples.getActivation(distance, angle);
901  break;
902  case PhyObject2DWrapper::BigCylinder:
903  case PhyObject2DWrapper::WheeledRobot:
904  actIt = m_roundSamples.getActivation(distance, angle);
905  break;
906  default:
907  Logger::warning("The sampled infrared sensor only works with Small Cylinders, Big Cylinders, Walls and other Robots");
908  continue;
909  }
910 
911  // Adding activations in the activations vector
912  for (int i = 0; i < activations.size(); ++i, ++actIt) {
913  activations[i] = min(1.0, activations[i] + (real(*actIt) / 1024.0));
914  }
915  }
916 
917  // Finally activating neurons
918  m_neuronsIterator->setCurrentBlock(name());
919  for (int i = 0; i < m_activeSensors.size(); i++) {
920  if (m_activeSensors[i]) {
921  m_neuronsIterator->setInput(activations[i]);
922  m_neuronsIterator->nextNeuron();
923  }
924  }
925 }
926 
928 {
929  return m_numActiveSensors;
930 }
931 
932 void MarXbotSampledProximityIRSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
933 {
934  // Calling parent function
935  MarXbotSensor::resourceChanged(resourceName, changeType);
936 
937  if (changeType == Deleted) {
938  return;
939  }
940 
941  if (resourceName == m_marxbotResource) {
942  m_robot = getResource<MarXbot>();
943 
944  // Disabling proximity IR sensors, they are not used here
945  m_robot->proximityIRSensorController()->setEnabled(false);
946  } else if (resourceName == m_neuronsIteratorResource) {
947  m_neuronsIterator = getResource<NeuronsIterator>();
948  m_neuronsIterator->setCurrentBlock(name());
949 
950  // We use the actual sensor ID in the neuron name
951  for (int i = 0; i < m_activeSensors.size(); i++) {
952  if (m_activeSensors[i]) {
953  m_neuronsIterator->setGraphicProperties("ir" + QString::number(i), 0.0, 1.0, Qt::red);
954  m_neuronsIterator->nextNeuron();
955  }
956  }
957  } else if (resourceName == "arena") {
958  // Storing the pointer to the arena
959  m_arena = getResource<Arena>();
960  } else {
961  Logger::info("Unknown resource " + resourceName + " for " + name());
962  }
963 }
964 
966  MarXbotSensor(params, prefix),
967  m_robot(NULL),
968  m_neuronsIterator(NULL),
969  m_enablePosition(ConfigurationHelper::getBool(params, prefix + "enablePosition", true)),
970  m_enableStatus(ConfigurationHelper::getBool(params, prefix + "enableStatus", true)),
971  m_enableAttached(ConfigurationHelper::getBool(params, prefix + "enableAttached", true)),
972  m_enableOtherAttached(ConfigurationHelper::getBool(params, prefix + "enableOtherAttached", true)),
973  m_numInputs((m_enablePosition ? 2 : 0) + (m_enableStatus ? 1 : 0) + (m_enableAttached ? 1 : 0) + (m_enableOtherAttached ? 1 : 0))
974 {
975 }
976 
978 {
979  // Nothing to do here
980 }
981 
983 {
984  // Calling parent function
985  MarXbotSensor::save(params, prefix);
986 
987  // Saving parameters
988  params.startObjectParameters(prefix, "MarXbotAttachmentDeviceSensor", this);
989  params.createParameter(prefix, "enablePosition", (m_enablePosition ? "true" : "false"));
990  params.createParameter(prefix, "enableStatus", (m_enableStatus ? "true" : "false"));
991  params.createParameter(prefix, "enableAttached", (m_enableAttached ? "true" : "false"));
992  params.createParameter(prefix, "enableOtherAttached", (m_enableOtherAttached ? "true" : "false"));
993 }
994 
996 {
997  // Calling parent function
999 
1000  // Describing our parameters
1001  Descriptor d = addTypeDescription(type, "The sensor providing the attachment device proprioception", "This sensor provides information about the current status of the attachment device. It provides several inputs (which can be disabled using parameters). The position of the gripper is provided using two neurons (the sin() and the cos() of the angle) to alleviate the problem of 0° and 360° being the same angle with opposite activations.");
1002  d.describeBool("enablePosition").def(true).help("Whether inputs with the position of the attachment device should be enabled or not (default: true)");
1003  d.describeBool("enableStatus").def(true).help("Whether the input with the status of the attachment device should be enabled or not (default: true)");
1004  d.describeBool("enableAttached").def(true).help("Whether the input telling if this robot is attached to another robot should be enabled or not (default: true)");
1005  d.describeBool("enableOtherAttached").def(true).help("Whether the input telling if another robot is attached to this robot should be enabled or not (default: true)");
1006 }
1007 
1009 {
1010  // Checking all resources we need exist
1012 
1013  // Acquiring the lock to get resources
1014  ResourcesLocker locker(this);
1015 
1016  // Getting the motor controller for the attachment device
1018 
1019  // Now reading values
1020  m_neuronsIterator->setCurrentBlock(name());
1021  if (m_enablePosition) {
1022  const real pos = ctrl->getPosition();
1023  m_neuronsIterator->setInput(sin(pos) * 0.5 + 0.5);
1024  m_neuronsIterator->nextNeuron();
1025  m_neuronsIterator->setInput(cos(pos) * 0.5 + 0.5);
1026  m_neuronsIterator->nextNeuron();
1027  }
1028  if (m_enableStatus) {
1029  switch (ctrl->getStatus()) {
1030  case MarXbotAttachmentDeviceMotorController::Open:
1031  m_neuronsIterator->setInput(0.0);
1032  break;
1033  case MarXbotAttachmentDeviceMotorController::HalfClosed:
1034  m_neuronsIterator->setInput(0.5);
1035  break;
1036  case MarXbotAttachmentDeviceMotorController::Closed:
1037  m_neuronsIterator->setInput(1.0);
1038  break;
1039  }
1040  m_neuronsIterator->nextNeuron();
1041  }
1042  if (m_enableAttached) {
1043  if (ctrl->attachedToRobot()) {
1044  m_neuronsIterator->setInput(1.0);
1045  } else {
1046  m_neuronsIterator->setInput(0.0);
1047  }
1048  m_neuronsIterator->nextNeuron();
1049  }
1050  if (m_enableOtherAttached) {
1051  if (ctrl->otherAttachedToUs()) {
1052  m_neuronsIterator->setInput(1.0);
1053  } else {
1054  m_neuronsIterator->setInput(0.0);
1055  }
1056  m_neuronsIterator->nextNeuron();
1057  }
1058 }
1059 
1061 {
1062  return m_numInputs;
1063 }
1064 
1065 void MarXbotAttachmentDeviceSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
1066 {
1067  // Calling parent function
1068  MarXbotSensor::resourceChanged(resourceName, changeType);
1069 
1070  if (changeType == Deleted) {
1071  return;
1072  }
1073 
1074  if (resourceName == m_marxbotResource) {
1075  m_robot = getResource<MarXbot>();
1076 
1077  // Enabling the attachment device and its motor controller
1078  m_robot->enableAttachmentDevice(true);
1079  m_robot->attachmentDeviceController()->setEnabled(true);
1080  } else if (resourceName == m_neuronsIteratorResource) {
1081  m_neuronsIterator = getResource<NeuronsIterator>();
1082  m_neuronsIterator->setCurrentBlock(name());
1083 
1084  // Creating labels depending on which inputs are activated
1085  if (m_enablePosition) {
1086  m_neuronsIterator->setGraphicProperties("aps", 0.0, 1.0, Qt::red);
1087  m_neuronsIterator->nextNeuron();
1088  m_neuronsIterator->setGraphicProperties("apc", 0.0, 1.0, Qt::red);
1089  m_neuronsIterator->nextNeuron();
1090  }
1091  if (m_enableStatus) {
1092  m_neuronsIterator->setGraphicProperties("as", 0.0, 1.0, Qt::red);
1093  m_neuronsIterator->nextNeuron();
1094  }
1095  if (m_enableAttached) {
1096  m_neuronsIterator->setGraphicProperties("aa", 0.0, 1.0, Qt::red);
1097  m_neuronsIterator->nextNeuron();
1098  }
1099  if (m_enableOtherAttached) {
1100  m_neuronsIterator->setGraphicProperties("ao", 0.0, 1.0, Qt::red);
1101  m_neuronsIterator->nextNeuron();
1102  }
1103  } else {
1104  Logger::info("Unknown resource " + resourceName + " for " + name());
1105  }
1106 }
1107 
1108 }