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 "mathutils.h"
30 #include "utilitiesexceptions.h"
31 #include <QMutex>
32 #include <limits>
33 
34 namespace farsa {
35 
37  Sensor(params, prefix),
38  m_marxbotResource("robot"),
39  m_neuronsIteratorResource("neuronsIterator")
40 {
41  // Reading parameters
44 
45  // Declaring the resources that are needed here
47 }
48 
50 {
51  // Nothing to do here
52 }
53 
54 void MarXbotSensor::save(ConfigurationParameters& params, QString prefix)
55 {
56  // Calling parent function
57  Sensor::save(params, prefix);
58 
59  // Saving parameters
60  params.startObjectParameters(prefix, "MarXbotSensor", this);
61  params.createParameter(prefix, "marxbot", m_marxbotResource);
62  params.createParameter(prefix, "neuronsIterator", m_neuronsIteratorResource);
63 }
64 
65 void MarXbotSensor::describe(QString type)
66 {
67  // Calling parent function
68  Sensor::describe(type);
69 
70  // Describing our parameters
71  Descriptor d = addTypeDescription(type, "The base class for MarXbot sensors");
72  d.describeString("marxbot").def("robot").help("The name of the resource associated with the MarXbot robot to use (default is \"robot\")");
73  d.describeString("neuronsIterator").def("neuronsIterator").help("The name of the resource associated with the neural network iterator (default is \"neuronsIterator\")");
74 }
75 
76 void MarXbotSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
77 {
78  // Calling parent function
79  Sensor::resourceChanged(resourceName, changeType);
80 
81  // Here we only check whether the resource has been deleted and reset the check flag, the
82  // actual work is done in subclasses
83  if (changeType == Deleted) {
85  return;
86  }
87 }
88 
90  MarXbotSensor(params, prefix),
91  m_robot(NULL),
92  m_neuronsIterator(NULL),
93  m_activeSensors(ConfigurationHelper::getBoolVector(params, prefix + "activeSensors", "111111111111111111111111")),
94  m_numActiveSensors(m_activeSensors.count(true))
95 {
96  if (m_activeSensors.size() != 24) {
97  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)");
98  }
99 }
100 
102 {
103  // Nothing to do here
104 }
105 
107 {
108  // Calling parent function
109  MarXbotSensor::save(params, prefix);
110 
111  // Saving parameters
112  params.startObjectParameters(prefix, "MarXbotProximityIRSensor", this);
113  QString activeSensorsString;
114  for (int i = 0; i < m_activeSensors.size(); i++) {
115  activeSensorsString += (m_activeSensors[i] ? "1" : "0");
116  }
117  params.createParameter(prefix, "activeSensors", activeSensorsString);
118 }
119 
121 {
122  // Calling parent function
124 
125  // Describing our parameters
126  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");
127  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)");
128 }
129 
131 {
132  // Checking all resources we need exist
134 
135  // Acquiring the lock to get resources
136  ResourcesLocker locker( this );
137 
138  m_neuronsIterator->setCurrentBlock(name());
139  for (int i = 0; i < m_activeSensors.size(); i++) {
140  if (m_activeSensors[i]) {
141  m_neuronsIterator->setInput(applyNoise(m_robot->proximityIRSensorController()->activation(i), 0.0, 1.0));
142  m_neuronsIterator->nextNeuron();
143  }
144  }
145 }
146 
148 {
149  return m_numActiveSensors;
150 }
151 
152 void MarXbotProximityIRSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
153 {
154  // Calling parent function
155  MarXbotSensor::resourceChanged(resourceName, changeType);
156 
157  if (changeType == Deleted) {
158  return;
159  }
160 
161  if (resourceName == m_marxbotResource) {
162  m_robot = getResource<PhyMarXbot>();
163 
164  // Eanbling sensors
165  m_robot->proximityIRSensorController()->setEnabled(true);
166 
167  // Now enabling/disabling individual sensors depending on the m_activeSensors vector
168  for (int i = 0; i < m_activeSensors.size(); i++) {
169  m_robot->proximityIRSensorController()->setSensorActive(i, m_activeSensors[i]);
170  }
171  } else if (resourceName == m_neuronsIteratorResource) {
172  m_neuronsIterator = getResource<NeuronsIterator>();
173  m_neuronsIterator->setCurrentBlock(name());
174 
175  // We use the actual sensor ID in the neuron name
176  for (int i = 0; i < m_activeSensors.size(); i++) {
177  if (m_activeSensors[i]) {
178  m_neuronsIterator->setGraphicProperties("ir" + QString::number(i), 0.0, 1.0, Qt::red);
179  m_neuronsIterator->nextNeuron();
180  }
181  }
182  } else {
183  Logger::info("Unknown resource " + resourceName + " for " + name());
184  }
185 }
186 
188  MarXbotSensor(params, prefix),
189  m_robot(NULL),
190  m_neuronsIterator(NULL),
191  m_arena(NULL),
192  m_invertActivation(ConfigurationHelper::getBool(params, prefix + "invertActivation", false))
193 {
194  // Here we also need the arena to work
195  addUsableResource("arena");
196 }
197 
199 {
200  // Nothing to do here
201 }
202 
204 {
205  // Calling parent function
206  MarXbotSensor::save(params, prefix);
207 
208  // Saving parameters
209  params.startObjectParameters(prefix, "MarXbotGroundBottomIRSensor", this);
210  params.createParameter(prefix, "invertActivation", (m_invertActivation ? "true" : "false"));
211 }
212 
214 {
215  // Calling parent function
217 
218  // Describing our parameters
219  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.");
220  d.describeBool("invertActivation").def(false).help("If true inverts the activation of the sensors", "If true the sensor is activated with 0.0 above white and with 1.0 above black, if false the opposite holds. The default value is false");
221 }
222 
224 {
225  // Checking all resources we need exist
227 
228  // Acquiring the lock to get resources
229  ResourcesLocker locker( this );
230 
231  // Setting neurons activations
232  m_neuronsIterator->setCurrentBlock(name());
233  for (int i = 0; i < size(); i++, m_neuronsIterator->nextNeuron()) {
234  const wVector sensorPosition = m_robot->matrix().transformVector(m_robot->groundBottomIRSensorController()->sensors()[i].getPosition());
235  const QColor color = getColorAtArenaGroundPosition(m_arena, sensorPosition);
236  const real actv = real(qGray(color.rgb())) / 255.0;
237 
238  if (m_invertActivation) {
239  m_neuronsIterator->setInput(applyNoise(1.0 - actv, 0.0, 1.0));
240  } else {
241  m_neuronsIterator->setInput(applyNoise(actv, 0.0, 1.0));
242  }
243  }
244 }
245 
247 {
248  return 4;
249 }
250 
251 void MarXbotGroundBottomIRSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
252 {
253  // Calling parent function
254  MarXbotSensor::resourceChanged(resourceName, changeType);
255 
256  if (changeType == Deleted) {
257  return;
258  }
259 
260  if (resourceName == m_marxbotResource) {
261  m_robot = getResource<PhyMarXbot>();
262 
263  // Eanbling sensors
264  m_robot->groundBottomIRSensorController()->setEnabled(true);
265  } else if (resourceName == m_neuronsIteratorResource) {
266  m_neuronsIterator = getResource<NeuronsIterator>();
267  m_neuronsIterator->setCurrentBlock(name());
268  for (int i = 0; i < size(); i++, m_neuronsIterator->nextNeuron()) {
269  m_neuronsIterator->setGraphicProperties("gb" + QString::number(i), 0.0, 1.0, Qt::red);
270  }
271  } else if (resourceName == "arena") {
272  // Storing the pointer to the arena
273  m_arena = getResource<Arena>();
274  } else {
275  Logger::info("Unknown resource " + resourceName + " for " + name());
276  }
277 }
278 
280  MarXbotSensor(params, prefix),
281  m_robot(NULL),
282  m_neuronsIterator(NULL),
283  m_arena(NULL)
284 {
285  // Here we also need the arena to work
286  addUsableResource("arena");
287 }
288 
290 {
291  // Nothing to do here
292 }
293 
295 {
296  // Calling parent function
297  MarXbotSensor::save(params, prefix);
298 
299  // Saving parameters
300  params.startObjectParameters(prefix, "MarXbotGroundAroundIRSensor", this);
301 }
302 
304 {
305  // Calling parent function
307 
308  // Describing our parameters
309  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).");
310 }
311 
313 {
314  // Checking all resources we need exist
316 
317  // Acquiring the lock to get resources
318  ResourcesLocker locker( this );
319 
320  // Setting neurons activations
321  m_neuronsIterator->setCurrentBlock(name());
322  for (int i = 0; i < size(); i++, m_neuronsIterator->nextNeuron()) {
323  const wVector sensorPosition = m_robot->matrix().transformVector(m_robot->groundAroundIRSensorController()->sensors()[i].getPosition());
324  const QColor color = getColorAtArenaGroundPosition(m_arena, sensorPosition);
325  const real actv = real(qGray(color.rgb())) / 255.0;
326 
327  m_neuronsIterator->setInput(applyNoise(actv, 0.0, 1.0));
328  }
329 }
330 
332 {
333  return 8;
334 }
335 
336 void MarXbotGroundAroundIRSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
337 {
338  // Calling parent function
339  MarXbotSensor::resourceChanged(resourceName, changeType);
340 
341  if (changeType == Deleted) {
342  return;
343  }
344 
345  if (resourceName == m_marxbotResource) {
346  m_robot = getResource<PhyMarXbot>();
347 
348  // Eanbling sensors
349  m_robot->groundAroundIRSensorController()->setEnabled(true);
350  } else if (resourceName == m_neuronsIteratorResource) {
351  m_neuronsIterator = getResource<NeuronsIterator>();
352  m_neuronsIterator->setCurrentBlock(name());
353  for (int i = 0; i < size(); i++, m_neuronsIterator->nextNeuron()) {
354  m_neuronsIterator->setGraphicProperties("ga" + QString::number(i), 0.0, 1.0, Qt::red);
355  }
356  } else if (resourceName == "arena") {
357  // Storing the pointer to the arena
358  m_arena = getResource<Arena>();
359  } else {
360  Logger::info("Unknown resource " + resourceName + " for " + name());
361  }
362 }
363 
365  MarXbotSensor(params, prefix),
366  m_robot(NULL),
367  m_neuronsIterator(NULL),
368  m_numReceptors(ConfigurationHelper::getInt(params, prefix + "numReceptors", 8)),
369  m_useRedChannel(ConfigurationHelper::getBool(params, prefix + "useRedChannel", true)),
370  m_useGreenChannel(ConfigurationHelper::getBool(params, prefix + "useGreenChannel", true)),
371  m_useBlueChannel(ConfigurationHelper::getBool(params, prefix + "useBlueChannel", true)),
372  m_usedColorChannels((m_useRedChannel ? 1 : 0) + (m_useGreenChannel ? 1 : 0) + (m_useBlueChannel ? 1 : 0)),
373  m_aperture(ConfigurationHelper::getDouble(params, prefix + "aperture", 360.0f)),
374  m_maxDistance(ConfigurationHelper::getDouble(params, prefix + "maxDistance", +Infinity)),
375  m_camera(NULL),
376  m_drawCamera(ConfigurationHelper::getBool(params, prefix + "drawCamera", true))
377 {
378  // Few sanity checks
379  if (m_numReceptors <= 0) {
380  ConfigurationHelper::throwUserConfigError(prefix + "numReceptors", params.getValue(prefix + "numReceptors"), "The parameter must be an integer greater or equal to 1");
381  }
382  if ((m_aperture < 0.0f) || (m_aperture > 360.0f)) {
383  ConfigurationHelper::throwUserConfigError(prefix + "aperture", params.getValue(prefix + "aperture"), "The parameter must be a real number between 0 and 360");
384  }
385 }
386 
388 {
389  // Deleting the camera
390  delete m_camera;
391 }
392 
394 {
395  // Calling parent function
396  MarXbotSensor::save(params, prefix);
397 
398  // Saving parameters
399  params.startObjectParameters(prefix, "MarXbotLinearCameraSensor", this);
400  params.createParameter(prefix, "numReceptors", QString::number(m_numReceptors));
401  params.createParameter(prefix, "useRedChannel", (m_useRedChannel ? "true" : "false"));
402  params.createParameter(prefix, "useGreenChannel", (m_useGreenChannel ? "true" : "false"));
403  params.createParameter(prefix, "useBlueChannel", (m_useBlueChannel ? "true" : "false"));
404  params.createParameter(prefix, "aperture", QString::number(m_aperture));
405  params.createParameter(prefix, "maxDistance", QString::number(m_maxDistance));
406  params.createParameter(prefix, "drawCamera", (m_drawCamera ? "true" : "false"));
407 }
408 
410 {
411  // Calling parent function
413 
414  // Describing our parameters
415  Descriptor d = addTypeDescription(type, "The linear camera sensor of the MarXbot robot", "This is a 360° linear camera");
416  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)");
417  d.describeBool("useRedChannel").def(true).help("Whether the red component of the perceived objects should be used or not (default true)");
418  d.describeBool("useGreenChannel").def(true).help("Whether the green component of the perceived objects should be used or not (default true)");
419  d.describeBool("useBlueChannel").def(true).help("Whether the blue component of the perceived objects should be used or not (default true)");
420  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)");
421  d.describeReal("maxDistance").def(+Infinity).limits(0.0f, +Infinity).help("The distance above which objects are not seen by the camera in meters");
422  d.describeBool("drawCamera").def(true).help("Whether to draw the camera or not");
423 }
424 
426 {
427  // Checking all resources we need exist
429 
430  // Acquiring the lock to get resources
431  ResourcesLocker locker(this);
432 
433  // Updating the camera
434  m_camera->update();
435 
436  // Reading activations: first the red one, then the green one and finally the blue one
437  m_neuronsIterator->setCurrentBlock(name());
438  if (m_useRedChannel) {
439  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
440  m_neuronsIterator->setInput(applyNoise(m_camera->colorForReceptor(i).redF(), 0.0, 1.0));
441  }
442  }
443  if (m_useGreenChannel) {
444  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
445  m_neuronsIterator->setInput(applyNoise(m_camera->colorForReceptor(i).greenF(), 0.0, 1.0));
446  }
447  }
448  if (m_useBlueChannel) {
449  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
450  m_neuronsIterator->setInput(applyNoise(m_camera->colorForReceptor(i).blueF(), 0.0, 1.0));
451  }
452  }
453 }
454 
456 {
457  return m_numReceptors * m_usedColorChannels;
458 }
459 
460 void MarXbotLinearCameraSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
461 {
462  // Calling parent function
463  MarXbotSensor::resourceChanged(resourceName, changeType);
464 
465  if (changeType == Deleted) {
466  // Deleting the camera if the robot was deleted
467  if (resourceName == m_marxbotResource) {
468  delete m_camera;
469  m_camera = NULL;
470  }
471 
472  return;
473  }
474 
475  if (resourceName == m_marxbotResource) {
476  m_robot = getResource<PhyMarXbot>();
477 
478  // Now we can also create the camera
479  wMatrix mtr = wMatrix::roll(-PI_GRECO / 2.0);
480 #if defined(__GNUC__) && defined(DEVELOPER_WARNINGS)
481  #warning QUI INVECE DI UNA COSTANTE, CALCOLARSI UNA POSIZIONE DALLE DIMENSIONI DEL ROBOT
482 #endif
483  mtr.w_pos.z = 0.13f;
484  m_camera = new LinearCamera(m_robot, mtr, toRad(m_aperture), m_numReceptors, m_maxDistance, Qt::black);
485 
486  // Sharing resources with the camera
487  m_camera->shareResourcesWith(this);
488  m_camera->drawCamera(m_drawCamera);
489  } else if (resourceName == m_neuronsIteratorResource) {
490  m_neuronsIterator = getResource<NeuronsIterator>();
491  m_neuronsIterator->setCurrentBlock(name());
492  if (m_useRedChannel) {
493  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
494  m_neuronsIterator->setGraphicProperties("lr" + QString::number(i), 0.0, 1.0, Qt::red);
495  }
496  }
497  if (m_useGreenChannel) {
498  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
499  m_neuronsIterator->setGraphicProperties("lg" + QString::number(i), 0.0, 1.0, Qt::red);
500  }
501  }
502  if (m_useBlueChannel) {
503  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
504  m_neuronsIterator->setGraphicProperties("lb" + QString::number(i), 0.0, 1.0, Qt::red);
505  }
506  }
507  } else {
508  Logger::info("Unknown resource " + resourceName + " for " + name());
509  }
510 }
511 
513  MarXbotSensor(params, prefix),
514  m_robot(NULL),
515  m_neuronsIterator(NULL),
516  m_numReceptors(ConfigurationHelper::getInt(params, prefix + "numReceptors", 8)),
517  m_useRedChannel(ConfigurationHelper::getBool(params, prefix + "useRedChannel", true)),
518  m_useGreenChannel(ConfigurationHelper::getBool(params, prefix + "useGreenChannel", true)),
519  m_useBlueChannel(ConfigurationHelper::getBool(params, prefix + "useBlueChannel", true)),
520  m_usedColorChannels((m_useRedChannel ? 1 : 0) + (m_useGreenChannel ? 1 : 0) + (m_useBlueChannel ? 1 : 0)),
521  m_aperture(ConfigurationHelper::getDouble(params, prefix + "aperture", 360.0f)),
522  m_maxDistance(ConfigurationHelper::getDouble(params, prefix + "maxDistance", +Infinity)),
523  m_receptorsRanges(),
524  m_camera(NULL),
525  m_drawCamera(ConfigurationHelper::getBool(params, prefix + "drawCamera", true))
526 {
527  // We have to get here the receptorsRanges parameter
528  QString receptorsRangesStr = params.getValue(prefix + "receptorsRanges");
529  if (receptorsRangesStr.isEmpty()) {
530  // Here we use aperture and numReceptors, doing few sanity checks
531  if (m_numReceptors <= 0) {
532  ConfigurationHelper::throwUserConfigError(prefix + "numReceptors", params.getValue(prefix + "numReceptors"), "The parameter must be an integer greater or equal to 1");
533  }
534  if ((m_aperture < 0.0f) || (m_aperture > 360.0f)) {
535  ConfigurationHelper::throwUserConfigError(prefix + "aperture", params.getValue(prefix + "aperture"), "The parameter must be a real number between 0 and 360");
536  }
537  } else {
538  // Here we use receptorsRanges, checking that neither aperture nor numReceptors is set
539  if (!params.getValue(prefix + "numReceptors").isEmpty() || !params.getValue(prefix + "aperture").isEmpty()) {
540  ConfigurationHelper::throwUserConfigError(prefix + "receptorsRanges", params.getValue(prefix + "receptorsRanges"), "You must not specify the receptorsRanges parameter if either aperture or numReceptors has been specified");
541  }
542 
543  // Now getting the parameter
544  bool ok;
545  m_receptorsRanges = SimpleInterval::vectorOfSimpleIntervalsFromString(receptorsRangesStr, &ok);
546  if (!ok) {
547  ConfigurationHelper::throwUserConfigError(prefix + "receptorsRanges", params.getValue(prefix + "receptorsRanges"), "The receptorsRanges parameter must be a comma separated list of simple intervals");
548  }
549 
550  // We also have to convert angles to radiants
551  for (int i = 0; i < m_receptorsRanges.size(); i++) {
552  m_receptorsRanges[i].start = toRad(m_receptorsRanges[i].start);
553  m_receptorsRanges[i].end = toRad(m_receptorsRanges[i].end);
554  }
555 
556  // Setting m_numReceptors to the correct value
557  m_numReceptors = m_receptorsRanges.size();
558  }
559 }
560 
562 {
563  // Deleting the camera
564  delete m_camera;
565 }
566 
568 {
569  // Calling parent function
570  MarXbotSensor::save(params, prefix);
571 
572  // Saving parameters
573  params.startObjectParameters(prefix, "MarXbotLinearCameraSensorNew", this);
574  params.createParameter(prefix, "numReceptors", QString::number(m_numReceptors));
575  params.createParameter(prefix, "aperture", QString::number(m_aperture));
576  params.createParameter(prefix, "maxDistance", QString::number(m_maxDistance));
577  params.createParameter(prefix, "useRedChannel", (m_useRedChannel ? "true" : "false"));
578  params.createParameter(prefix, "useGreenChannel", (m_useGreenChannel ? "true" : "false"));
579  params.createParameter(prefix, "useBlueChannel", (m_useBlueChannel ? "true" : "false"));
580  params.createParameter(prefix, "drawCamera", (m_drawCamera ? "true" : "false"));
581 }
582 
584 {
585  // Calling parent function
587 
588  // Describing our parameters
589  Descriptor d = addTypeDescription(type, "The linear camera sensor of the MarXbot robot (new implementation)", "This is a 360° linear camera");
590  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)");
591  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)");
592  d.describeReal("maxDistance").def(+Infinity).limits(0.0f, +Infinity).help("The distance above which objects are not seen by the camera in meters");
593  d.describeString("receptorsRanges").def("").help("The range for each receptor", "This parameter is a comma separated list of ranges (in the form [start, end]), each range being the aperture of a single receptor. The number of receptors is equal to the number of ranges. The start and end angles of the range are in degrees. Note that if you specify this parameter you must not specify the aperture neither the numReceptors parameters.");
594  d.describeBool("useRedChannel").def(true).help("Whether the red component of the perceived objects should be used or not (default true)");
595  d.describeBool("useGreenChannel").def(true).help("Whether the green component of the perceived objects should be used or not (default true)");
596  d.describeBool("useBlueChannel").def(true).help("Whether the blue component of the perceived objects should be used or not (default true)");
597  d.describeBool("drawCamera").def(true).help("Whether to draw the camera or not");
598 }
599 
601 {
602  // Checking all resources we need exist
604 
605  // Acquiring the lock to get resources
606  ResourcesLocker locker(this);
607 
608  // Updating the camera
609  m_camera->update();
610 
611  // Reading activations: first the red one, then the green one and finally the blue one
612  m_neuronsIterator->setCurrentBlock(name());
613  if (m_useRedChannel) {
614  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
615  m_neuronsIterator->setInput(applyNoise(m_camera->colorForReceptor(i).redF(), 0.0, 1.0));
616  }
617  }
618  if (m_useGreenChannel) {
619  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
620  m_neuronsIterator->setInput(applyNoise(m_camera->colorForReceptor(i).greenF(), 0.0, 1.0));
621  }
622  }
623  if (m_useBlueChannel) {
624  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
625  m_neuronsIterator->setInput(applyNoise(m_camera->colorForReceptor(i).blueF(), 0.0, 1.0));
626  }
627  }
628 }
629 
631 {
632  return m_numReceptors * m_usedColorChannels;
633 }
634 
635 void MarXbotLinearCameraSensorNew::resourceChanged(QString resourceName, ResourceChangeType changeType)
636 {
637  // Calling parent function
638  MarXbotSensor::resourceChanged(resourceName, changeType);
639 
640  if (changeType == Deleted) {
641  // Deleting the camera if the robot was deleted
642  if (resourceName == m_marxbotResource) {
643  delete m_camera;
644  m_camera = NULL;
645  }
646 
647  return;
648  }
649 
650  if (resourceName == m_marxbotResource) {
651  m_robot = getResource<PhyMarXbot>();
652 
653  // Now we can also create the camera
654  wMatrix mtr = wMatrix::roll(-PI_GRECO / 2.0);
655 #if defined(__GNUC__) && defined(DEVELOPER_WARNINGS)
656  #warning QUI INVECE DI UNA COSTANTE, CALCOLARSI UNA POSIZIONE DALLE DIMENSIONI DEL ROBOT
657 #endif
658  mtr.w_pos.z = 0.13f;
659  if (m_receptorsRanges.isEmpty()) {
660  m_camera = new LinearCameraNew::LinearCamera(m_robot, mtr, toRad(m_aperture), m_numReceptors, m_maxDistance, Qt::black);
661  } else {
662  m_camera = new LinearCameraNew::LinearCamera(m_robot, mtr, m_receptorsRanges, m_maxDistance, Qt::black);
663  }
664 
665  // Sharing resources with the camera
666  m_camera->shareResourcesWith(this);
667  m_camera->drawCamera(m_drawCamera);
668  } else if (resourceName == m_neuronsIteratorResource) {
669  m_neuronsIterator = getResource<NeuronsIterator>();
670  m_neuronsIterator->setCurrentBlock(name());
671  if (m_useRedChannel) {
672  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
673  m_neuronsIterator->setGraphicProperties("lr" + QString::number(i), 0.0, 1.0, Qt::red);
674  }
675  }
676  if (m_useGreenChannel) {
677  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
678  m_neuronsIterator->setGraphicProperties("lg" + QString::number(i), 0.0, 1.0, Qt::red);
679  }
680  }
681  if (m_useBlueChannel) {
682  for (int i = 0; i < m_numReceptors; i++, m_neuronsIterator->nextNeuron()) {
683  m_neuronsIterator->setGraphicProperties("lb" + QString::number(i), 0.0, 1.0, Qt::red);
684  }
685  }
686  } else {
687  Logger::info("Unknown resource " + resourceName + " for " + name());
688  }
689 }
690 
691 namespace __MarXbotTractionSensor_internal {
692  #ifndef GLMultMatrix
693  #define GLMultMatrix glMultMatrixf
694  // for double glMultMatrixd
695  #endif
696 
704  {
705  public:
730  TractionSensorGraphic(WObject *object, const wVector& offset, real radius, real scale = 1.0, real maxLength = 1.0, real minLength = 0.0, QString name = "unamed") :
731  GraphicalWObject(object->world(), name),
732  m_object(object),
733  m_radius(radius),
734  m_scale(scale),
735  m_maxLength(maxLength),
736  m_minLength(minLength),
738  m_vector(0.0, 0.0, 0.0),
739  m_vectorMutex()
740  {
741  // Attaching to object (which also becomes our owner). We also build the displacement matrix
742  wMatrix displacement = wMatrix::identity();
743  displacement.w_pos = offset;
744  attachToObject(m_object, true, displacement);
745 
746  // We also use our own color and texture
748  setTexture("");
749  }
750 
755  {
756  }
757 
766  void setVector(const wVector vector)
767  {
768  m_vectorMutex.lock();
769  m_vector = vector;
770  m_vectorMutex.unlock();
771  }
772 
773  protected:
782  virtual void render(RenderWObject* renderer, QGLContext* gw)
783  {
784  // Copying the m_activations vector to a local vector to avoid concurrent accesses.
785  m_vectorMutex.lock();
786  const wVector vector = m_vector;
787  m_vectorMutex.unlock();
788 
789  // Setting the matrix to draw the vector.
790  glPushMatrix();
791  GLMultMatrix(&tm[0][0]);
792 
793  bool xLimited;
794  bool yLimited;
795  const wVector vectorToDraw = wVector(limitComponent(vector.x, xLimited), limitComponent(vector.y, yLimited), vector.z).scale(m_scale);
796  const wVector xComponent(vectorToDraw.x, 0.0, vectorToDraw.z);
797  const wVector yComponent(0.0, vectorToDraw.y, vectorToDraw.z);
798 
799  // Drawing the three arrows (two for the axes, one for the resultant
800  drawArrow(xComponent, m_radius / 2.0, m_maxTipLength / 2.0, (xLimited ? Qt::red : Qt::cyan), renderer, gw);
801  drawArrow(yComponent, m_radius / 2.0, m_maxTipLength / 2.0, (yLimited ? Qt::red : Qt::cyan), renderer, gw);
802  drawArrow(vectorToDraw, m_radius, m_maxTipLength, Qt::cyan, renderer, gw);
803 
804  // Popping twice because we pushed both our matrix and the offset matrix
805  glPopMatrix();
806  glPopMatrix();
807  }
808 
823  void drawArrow(wVector vector, real radius, real maxTipLength, QColor col, RenderWObject* renderer, QGLContext* gw)
824  {
825  // Setting the color of the arrow
826  setColor(col);
827  renderer->container()->setupColorTexture(gw, renderer);
828 
829  const real vectorLength = vector.norm();
830  const wVector axis = vector.scale(1.0 / vectorLength);
831  const real bodyLength = vectorLength - maxTipLength;
832  const real tipLength = min(vectorLength, maxTipLength);
833  const real tipRadius = ((radius * 3.0) / 2.0) * (tipLength / maxTipLength);
834 
835  // First drawing the body if its length is positive
836  if (bodyLength > 0.0) {
837  RenderWObjectContainer::drawCylinder(axis, axis.scale(0.5 * bodyLength), bodyLength, radius, color());
838  }
839 
840  // Now drawing the tip as a cone:
841  {
842  GLUquadricObj *pObj;
843 
844  // opengl cylinder are aligned alogn the z axis, we want it along our axis,
845  // we create a rotation matrix to do the alignment. Moreover the cone must
846  // start at the end of the cylinder we draw before
847  const wVector tipDisplacement = (bodyLength < 0.0) ? wVector(0.0, 0.0, 0.0) : axis.scale(bodyLength);
848  glPushMatrix();
850  matrix.w_pos = tipDisplacement;
851  GLMultMatrix(&matrix[0][0]);
852 
853  // Get a new Quadric off the stack
854  pObj = gluNewQuadric();
855  gluQuadricTexture(pObj, true);
856  gluCylinder(pObj, tipRadius, 0, tipLength, 20, 2);
857 
858  // render the caps
859  gluQuadricOrientation(pObj, GLU_INSIDE);
860  gluDisk(pObj, 0.0f, tipRadius, 20, 1);
861 
862  gluDeleteQuadric(pObj);
863  glPopMatrix();
864  }
865  }
866 
876  real limitComponent(real v, bool& limited)
877  {
878  real l = fabs(v);
879  limited = false;
880  if (l < m_minLength) {
881  limited = true;
882  l = m_minLength;
883  } else if (l > m_maxLength) {
884  limited = true;
885  l = m_maxLength;
886  }
887  return (v < 0.0) ? -l : l;
888  }
889 
894 
899  const real m_radius;
900 
904  const real m_scale;
905 
914 
922 
929 
935  wVector m_vector;
936 
944  };
945 }
946 
947 using namespace __MarXbotTractionSensor_internal;
948 
950  MarXbotSensor(params, prefix),
951  m_robot(NULL),
952  m_neuronsIterator(NULL),
953  m_maxForce(ConfigurationHelper::getDouble(params, prefix + "maxForce", 1.0f)),
954  m_minForce(ConfigurationHelper::getDouble(params, prefix + "minForce", 0.0f)),
955  m_drawSensor(ConfigurationHelper::getBool(params, prefix + "drawSensor", true)),
956  m_graphics(NULL)
957 {
958  if (m_maxForce < 0.0) {
959  ConfigurationHelper::throwUserConfigError(prefix + "maxForce", params.getValue(prefix + "maxForce"), "The parameter must be a positive real number");
960  }
961  if (m_minForce < 0.0) {
962  ConfigurationHelper::throwUserConfigError(prefix + "minForce", params.getValue(prefix + "minForce"), "The parameter must be a positive real number");
963  }
964 }
965 
967 {
968  // Nothing to do here
969 }
970 
972 {
973  // Calling parent function
974  MarXbotSensor::save(params, prefix);
975 
976  // Saving parameters
977  params.startObjectParameters(prefix, "MarXbotTractionSensor", this);
978  params.createParameter(prefix, "maxForce", QString::number(m_maxForce));
979  params.createParameter(prefix, "minForce", QString::number(m_minForce));
980  params.createParameter(prefix, "drawSensor", m_drawSensor ? QString("true") : QString("false"));
981 }
982 
984 {
985  // Calling parent function
987 
988  // Describing our parameters
989  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");
990  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");
991  d.describeReal("minForce").def(0.0f).limits(0.0f, +Infinity).help("The minimum possible value of the force", "This is the value of the force on one axis under which the corresponding neuron is activated with 0");
992  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");
993 }
994 
996 {
997  // Checking all resources we need exist
999 
1000  // Acquiring the lock to get resources
1001  ResourcesLocker locker( this );
1002 
1003  m_neuronsIterator->setCurrentBlock(name());
1004  const wVector& t = m_robot->tractionSensorController()->traction();
1005 
1006  // The first two neurons correspond to the x axis, the second two to the y axis
1007  if (t.x > 0.0) {
1008  const real f = linearMap(t.x, m_minForce, m_maxForce, 0.0, 1.0);
1009  m_neuronsIterator->setInput(applyNoise(f, 0.0, 1.0));
1010  m_neuronsIterator->nextNeuron();
1011  m_neuronsIterator->setInput(0.0);
1012  m_neuronsIterator->nextNeuron();
1013  } else {
1014  const real f = linearMap(-t.x, m_minForce, m_maxForce, 0.0, 1.0);
1015  m_neuronsIterator->setInput(0.0);
1016  m_neuronsIterator->nextNeuron();
1017  m_neuronsIterator->setInput(applyNoise(f, 0.0, 1.0));
1018  m_neuronsIterator->nextNeuron();
1019  }
1020 
1021  if (t.y > 0.0) {
1022  const real f = linearMap(t.y, m_minForce, m_maxForce, 0.0, 1.0);
1023  m_neuronsIterator->setInput(applyNoise(f, 0.0, 1.0));
1024  m_neuronsIterator->nextNeuron();
1025  m_neuronsIterator->setInput(0.0);
1026  m_neuronsIterator->nextNeuron();
1027  } else {
1028  const real f = linearMap(-t.y, m_minForce, m_maxForce, 0.0, 1.0);
1029  m_neuronsIterator->setInput(0.0);
1030  m_neuronsIterator->nextNeuron();
1031  m_neuronsIterator->setInput(applyNoise(f, 0.0, 1.0));
1032  m_neuronsIterator->nextNeuron();
1033  }
1034 
1035  // Updating graphical representation if we have to
1036  if (m_drawSensor) {
1037  // We only draw the x and y components of the vector
1038  m_graphics->setVector(wVector(t.x, t.y, 0.0));
1039  }
1040 }
1041 
1043 {
1044  return 4;
1045 }
1046 
1047 void MarXbotTractionSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
1048 {
1049  // Calling parent function
1050  MarXbotSensor::resourceChanged(resourceName, changeType);
1051 
1052  if (changeType == Deleted) {
1053  return;
1054  }
1055 
1056  if (resourceName == m_marxbotResource) {
1057  m_robot = getResource<PhyMarXbot>();
1058 
1059  // Eanbling sensors
1060  m_robot->tractionSensorController()->setEnabled(true);
1061 
1062  // If graphics is enabled, creating the graphical object
1063  if (m_drawSensor) {
1064  const wVector offset(0.0, 0.0, PhyMarXbot::basez + PhyMarXbot::bodyh + 0.1);
1065 
1066  m_graphics = new TractionSensorGraphic(m_robot, offset, 0.005f, 0.1f / m_maxForce, m_maxForce, m_minForce, "Traction sensor");
1067  }
1068  } else if (resourceName == m_neuronsIteratorResource) {
1069  m_neuronsIterator = getResource<NeuronsIterator>();
1070  m_neuronsIterator->setCurrentBlock(name());
1071  m_neuronsIterator->setGraphicProperties("t+x", 0.0, 1.0, Qt::red);
1072  m_neuronsIterator->nextNeuron();
1073  m_neuronsIterator->setGraphicProperties("t-x", 0.0, 1.0, Qt::red);
1074  m_neuronsIterator->nextNeuron();
1075  m_neuronsIterator->setGraphicProperties("t+y", 0.0, 1.0, Qt::red);
1076  m_neuronsIterator->nextNeuron();
1077  m_neuronsIterator->setGraphicProperties("t-y", 0.0, 1.0, Qt::red);
1078  } else {
1079  Logger::info("Unknown resource " + resourceName + " for " + name());
1080  }
1081 }
1082 
1084  MarXbotSensor(params, prefix),
1085  m_robot(NULL),
1086  m_arena(NULL),
1087  m_neuronsIterator(NULL),
1088  m_activeSensors(ConfigurationHelper::getBoolVector(params, prefix + "activeSensors", "111111111111111111111111")),
1089  m_averageNum(ConfigurationHelper::getInt(params, prefix + "averageNum", 0)),
1090  m_numActiveSensors((m_averageNum == 0) ? m_activeSensors.count(true) : (24 / m_averageNum)),
1091  m_roundSamples(ConfigurationHelper::getString(params, prefix + "roundSamples", "round.sam")),
1092  m_smallSamples(ConfigurationHelper::getString(params, prefix + "smallSamples", "small.sam")),
1093  m_wallSamples(ConfigurationHelper::getString(params, prefix + "wallSamples", "wall.sam"))
1094 {
1095  if (m_activeSensors.size() != 24) {
1096  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)");
1097  }
1098 
1099  if ((m_averageNum != 0) && (m_averageNum != 2) && (m_averageNum != 3) && (m_averageNum != 4) && (m_averageNum != 6)) {
1100  ConfigurationHelper::throwUserConfigError(prefix + "averageNum", params.getValue(prefix + "averageNum"), "The parameter can only be 0, 2, 3, 4 or 6");
1101  }
1102 
1103  // Checking that the sampled files have the right number of IR sensors
1104  if (m_roundSamples.numIR() != 24) {
1105  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()));
1106  }
1107  if (m_smallSamples.numIR() != 24) {
1108  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()));
1109  }
1110  if (m_wallSamples.numIR() != 24) {
1111  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()));
1112  }
1113 
1114  // Here we also need the arena to work
1115  addUsableResource("arena");
1116 }
1117 
1119 {
1120  // Nothing to do here
1121 }
1122 
1124 {
1125  // Calling parent function
1126  MarXbotSensor::save(params, prefix);
1127 
1128  // Saving parameters
1129  params.startObjectParameters(prefix, "MarXbotSampledProximityIRSensor", this);
1130  QString activeSensorsString;
1131  for (int i = 0; i < m_activeSensors.size(); i++) {
1132  activeSensorsString += (m_activeSensors[i] ? "1" : "0");
1133  }
1134  params.createParameter(prefix, "activeSensors", activeSensorsString);
1135  params.createParameter(prefix, "averageNum", QString::number(m_averageNum));
1136  params.createParameter(prefix, "roundSamples", m_roundSamples.filename());
1137  params.createParameter(prefix, "smallSamples", m_smallSamples.filename());
1138  params.createParameter(prefix, "wallSamples", m_wallSamples.filename());
1139 }
1140 
1142 {
1143  // Calling parent function
1145 
1146  // Describing our parameters
1147  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");
1148  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)");
1149  d.describeInt("averageNum").def(0).limits(0, 6).help("How many sensors should be averaged", "This can only be 0, 2, 3, 4 or 6. If 2, for each couple of consecutive sensors the average is returned, if 3 for each triplet of consecutive sensors the average is returned and so on. If this is zero, no average is computed and the activeSensors parameter is used. The default value is 0");
1150  d.describeString("roundSamples").def("round.sam").help("The name of the file with samples for big round objects");
1151  d.describeString("smallSamples").def("small.sam").help("The name of the file with samples for small round objects");
1152  d.describeString("wallSamples").def("wall.sam").help("The name of the file with samples for walls");
1153 }
1154 
1156 {
1157  // Checking all resources we need exist
1159 
1160  // Acquiring the lock to get resources
1161  ResourcesLocker locker(this);
1162 
1163  // Getting the list of objects in the arena
1164  const QVector<PhyObject2DWrapper*>& objectsList = m_arena->getObjects();
1165 
1166  // Preparing the vector with activations and setting all values to 0
1167  QVector<real> activations(m_activeSensors.size(), 0.0);
1168 
1169  // Cycling through the list of objects. We first need to get the current position and orientation of the robot
1170  //const wVector robotPos = m_robot->position();
1171  //const real robotAng = m_robot->orientation(m_arena->getPlane());
1172  foreach(const PhyObject2DWrapper* obj, objectsList) {
1173  // Computing angle and distance. We don't need to remove the robot to which this sensor belongs because
1174  // the calculatations will give a negative distance
1175  double distance;
1176  double angle;
1177 
1178  // If computeDistanceAndOrientationFromRobot returns false, we have to discard this object
1179  if (!obj->computeDistanceAndOrientationFromRobot(*(m_arena->getRobotWrapper(m_marxbotResource)), distance, angle)) {
1180  continue;
1181  }
1182 
1183  // Getting the activation. The switch is to understand which samples to use
1184  QVector<unsigned int>::const_iterator actIt = QVector<unsigned int>::const_iterator();
1185  switch (obj->type()) {
1186  case PhyObject2DWrapper::Wall:
1187  actIt = m_wallSamples.getActivation(distance, angle);
1188  break;
1189  case PhyObject2DWrapper::SmallCylinder:
1190  actIt = m_smallSamples.getActivation(distance, angle);
1191  break;
1192  case PhyObject2DWrapper::BigCylinder:
1193  case PhyObject2DWrapper::WheeledRobot:
1194  actIt = m_roundSamples.getActivation(distance, angle);
1195  break;
1196  default:
1197  //Logger::warning("The sampled infrared sensor only works with Small Cylinders, Big Cylinders, Walls and other Robots");
1198  continue;
1199  }
1200 
1201  // Adding activations in the activations vector
1202  for (QVector<real>::iterator it = activations.begin(); it != activations.end(); ++it, ++actIt) {
1203  *it = min(1.0, *it + (real(*actIt) / 1024.0));
1204  }
1205  }
1206 
1207  // Finally activating neurons
1208  m_neuronsIterator->setCurrentBlock(name());
1209  if (m_averageNum == 0) {
1210  QVector<real>::const_iterator it = activations.constBegin();
1211  QVector<bool>::const_iterator activeIt = m_activeSensors.constBegin();
1212  while (activeIt != m_activeSensors.constEnd()) {
1213  if (*activeIt) {
1214  m_neuronsIterator->setInput(applyNoise(*it, 0.0, 1.0));
1215  m_neuronsIterator->nextNeuron();
1216  }
1217 
1218  ++it;
1219  ++activeIt;
1220  }
1221  } else {
1222  QVector<real>::const_iterator it = activations.constBegin();
1223  while (it != activations.constEnd()) {
1224  real avg = 0.0;
1225  for (int j = 0; j < m_averageNum; ++j, ++it) {
1226  avg += *it;
1227  }
1228  avg /= real(m_averageNum);
1229  m_neuronsIterator->setInput(applyNoise(avg, 0.0, 1.0));
1230  m_neuronsIterator->nextNeuron();
1231  }
1232  }
1233 }
1234 
1236 {
1237  return m_numActiveSensors;
1238 }
1239 
1240 void MarXbotSampledProximityIRSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
1241 {
1242  // Calling parent function
1243  MarXbotSensor::resourceChanged(resourceName, changeType);
1244 
1245  if (changeType == Deleted) {
1246  return;
1247  }
1248 
1249  if (resourceName == m_marxbotResource) {
1250  m_robot = getResource<MarXbot>();
1251 
1252  // Disabling proximity IR sensors, they are not used here
1253  m_robot->proximityIRSensorController()->setEnabled(false);
1254  } else if (resourceName == m_neuronsIteratorResource) {
1255  m_neuronsIterator = getResource<NeuronsIterator>();
1256  m_neuronsIterator->setCurrentBlock(name());
1257 
1258  // We use the actual sensor ID in the neuron name
1259  if (m_averageNum == 0) {
1260  for (int i = 0; i < m_activeSensors.size(); i++) {
1261  if (m_activeSensors[i]) {
1262  m_neuronsIterator->setGraphicProperties("ir" + QString::number(i), 0.0, 1.0, Qt::red);
1263  m_neuronsIterator->nextNeuron();
1264  }
1265  }
1266  } else {
1267  for (int i = 0; i < m_activeSensors.size(); i += m_averageNum) {
1268  m_neuronsIterator->setGraphicProperties(QString("ir%1-%2").arg(i).arg(i + m_averageNum - 1), 0.0, 1.0, Qt::red);
1269  m_neuronsIterator->nextNeuron();
1270  }
1271  }
1272  } else if (resourceName == "arena") {
1273  // Storing the pointer to the arena
1274  m_arena = getResource<Arena>();
1275  } else {
1276  Logger::info("Unknown resource " + resourceName + " for " + name());
1277  }
1278 }
1279 
1281  MarXbotSensor(params, prefix),
1282  m_robot(NULL),
1283  m_neuronsIterator(NULL),
1284  m_enablePosition(ConfigurationHelper::getBool(params, prefix + "enablePosition", true)),
1285  m_enableStatus(ConfigurationHelper::getBool(params, prefix + "enableStatus", true)),
1286  m_enableAttached(ConfigurationHelper::getBool(params, prefix + "enableAttached", true)),
1287  m_enableOtherAttached(ConfigurationHelper::getBool(params, prefix + "enableOtherAttached", true)),
1288  m_numInputs((m_enablePosition ? 2 : 0) + (m_enableStatus ? 1 : 0) + (m_enableAttached ? 1 : 0) + (m_enableOtherAttached ? 1 : 0))
1289 {
1290 }
1291 
1293 {
1294  // Nothing to do here
1295 }
1296 
1298 {
1299  // Calling parent function
1300  MarXbotSensor::save(params, prefix);
1301 
1302  // Saving parameters
1303  params.startObjectParameters(prefix, "MarXbotAttachmentDeviceSensor", this);
1304  params.createParameter(prefix, "enablePosition", (m_enablePosition ? "true" : "false"));
1305  params.createParameter(prefix, "enableStatus", (m_enableStatus ? "true" : "false"));
1306  params.createParameter(prefix, "enableAttached", (m_enableAttached ? "true" : "false"));
1307  params.createParameter(prefix, "enableOtherAttached", (m_enableOtherAttached ? "true" : "false"));
1308 }
1309 
1311 {
1312  // Calling parent function
1314 
1315  // Describing our parameters
1316  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.");
1317  d.describeBool("enablePosition").def(true).help("Whether inputs with the position of the attachment device should be enabled or not (default: true)");
1318  d.describeBool("enableStatus").def(true).help("Whether the input with the status of the attachment device should be enabled or not (default: true)");
1319  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)");
1320  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)");
1321 }
1322 
1324 {
1325  // Checking all resources we need exist
1327 
1328  // Acquiring the lock to get resources
1329  ResourcesLocker locker(this);
1330 
1331  // Getting the motor controller for the attachment device
1333 
1334  // Now reading values
1335  m_neuronsIterator->setCurrentBlock(name());
1336  if (m_enablePosition) {
1337  const real pos = ctrl->getPosition();
1338  m_neuronsIterator->setInput(sin(pos) * 0.5 + 0.5);
1339  m_neuronsIterator->nextNeuron();
1340  m_neuronsIterator->setInput(cos(pos) * 0.5 + 0.5);
1341  m_neuronsIterator->nextNeuron();
1342  }
1343  if (m_enableStatus) {
1344  switch (ctrl->getStatus()) {
1345  case MarXbotAttachmentDeviceMotorController::Open:
1346  m_neuronsIterator->setInput(0.0);
1347  break;
1348  case MarXbotAttachmentDeviceMotorController::HalfClosed:
1349  m_neuronsIterator->setInput(0.5);
1350  break;
1351  case MarXbotAttachmentDeviceMotorController::Closed:
1352  m_neuronsIterator->setInput(1.0);
1353  break;
1354  }
1355  m_neuronsIterator->nextNeuron();
1356  }
1357  if (m_enableAttached) {
1358  if (ctrl->attachedToRobot()) {
1359  m_neuronsIterator->setInput(1.0);
1360  } else {
1361  m_neuronsIterator->setInput(0.0);
1362  }
1363  m_neuronsIterator->nextNeuron();
1364  }
1365  if (m_enableOtherAttached) {
1366  if (ctrl->otherAttachedToUs()) {
1367  m_neuronsIterator->setInput(1.0);
1368  } else {
1369  m_neuronsIterator->setInput(0.0);
1370  }
1371  m_neuronsIterator->nextNeuron();
1372  }
1373 }
1374 
1376 {
1377  return m_numInputs;
1378 }
1379 
1380 void MarXbotAttachmentDeviceSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
1381 {
1382  // Calling parent function
1383  MarXbotSensor::resourceChanged(resourceName, changeType);
1384 
1385  if (changeType == Deleted) {
1386  return;
1387  }
1388 
1389  if (resourceName == m_marxbotResource) {
1390  m_robot = getResource<MarXbot>();
1391 
1392  // Enabling the attachment device and its motor controller
1393  m_robot->enableAttachmentDevice(true);
1394  m_robot->attachmentDeviceController()->setEnabled(true);
1395  } else if (resourceName == m_neuronsIteratorResource) {
1396  m_neuronsIterator = getResource<NeuronsIterator>();
1397  m_neuronsIterator->setCurrentBlock(name());
1398 
1399  // Creating labels depending on which inputs are activated
1400  if (m_enablePosition) {
1401  m_neuronsIterator->setGraphicProperties("aps", 0.0, 1.0, Qt::red);
1402  m_neuronsIterator->nextNeuron();
1403  m_neuronsIterator->setGraphicProperties("apc", 0.0, 1.0, Qt::red);
1404  m_neuronsIterator->nextNeuron();
1405  }
1406  if (m_enableStatus) {
1407  m_neuronsIterator->setGraphicProperties("as", 0.0, 1.0, Qt::red);
1408  m_neuronsIterator->nextNeuron();
1409  }
1410  if (m_enableAttached) {
1411  m_neuronsIterator->setGraphicProperties("aa", 0.0, 1.0, Qt::red);
1412  m_neuronsIterator->nextNeuron();
1413  }
1414  if (m_enableOtherAttached) {
1415  m_neuronsIterator->setGraphicProperties("ao", 0.0, 1.0, Qt::red);
1416  m_neuronsIterator->nextNeuron();
1417  }
1418  } else {
1419  Logger::info("Unknown resource " + resourceName + " for " + name());
1420  }
1421 }
1422 
1424 {
1425  switch (mode) {
1426  case AbsoluteMode:
1427  return "Absolute";
1428  case DeltaMode:
1429  return "Delta";
1430  default:
1431  return "Unknown";
1432  }
1433 
1434  return "UnknownMode";
1435 
1436 }
1437 
1439 {
1440  mode = mode.toUpper();
1441 
1442  if (mode == "ABSOLUTE") {
1443  return AbsoluteMode;
1444  } else if (mode == "DELTA") {
1445  return DeltaMode;
1446  } else {
1447  return UnknownMode;
1448  }
1449 
1450  return UnknownMode;
1451 }
1452 
1454  MarXbotSensor(params, prefix),
1455  m_robot(NULL),
1456  m_neuronsIterator(NULL),
1457  m_mode(stringToMode(ConfigurationHelper::getString(params, prefix + "mode", "Absolute")))
1458 {
1459  if (m_mode == UnknownMode) {
1460  ConfigurationHelper::throwUserConfigError(prefix + "mode", params.getValue(prefix + "mode"), "The parameter must be one of \"Absolute\" or \"Delta\" (case insensitive)");
1461  }
1462 }
1463 
1465 {
1466  // Nothing to do here
1467 }
1468 
1470 {
1471  // Calling parent function
1472  MarXbotSensor::save(params, prefix);
1473 
1474  // Saving parameters
1475  params.startObjectParameters(prefix, "MarXbotWheelSpeedsSensor", this);
1476  params.createParameter(prefix, "mode", modeToString(m_mode));
1477 }
1478 
1480 {
1481  // Calling parent function
1483 
1484  // Describing our parameters
1485  Descriptor d = addTypeDescription(type, "The sensor reporting the actual velocity of the wheels of the MarXbot", "This sensor have three modalities, see the mode parameter");
1486  d.describeEnum("mode").def("Absolute").values(QStringList() << "Absolute" << "Delta").help("The modality of the sensor", "The possible modalities are: \"Absolute\" meaning that the senors returns the current velocity of the wheels (scaled between -1 and 1) and \"Delta\", meaning that the sensor returns the absolute value of the difference between the desired velocity and the current velocity. The default value is \"Absolute\"");
1487 }
1488 
1490 {
1491  // Checking all resources we need exist
1493 
1494  // Acquiring the lock to get resources
1495  ResourcesLocker locker(this);
1496 
1497  // Getting the motor controller for the attachment device
1498  const WheelMotorController *const ctrl = m_robot->wheelsController();
1499 
1500  // Setting the current block to update the input neurons
1501  m_neuronsIterator->setCurrentBlock(name());
1502 
1503  // Now reading values of the current and desired velocity
1504  double curVel1;
1505  double curVel2;
1506  double desVel1;
1507  double desVel2;
1508  double minSpeed1;
1509  double minSpeed2;
1510  double maxSpeed1;
1511  double maxSpeed2;
1512  ctrl->getSpeeds(curVel1, curVel2);
1513  ctrl->getDesiredSpeeds(desVel1, desVel2);
1514  ctrl->getSpeedLimits(minSpeed1, minSpeed2, maxSpeed1, maxSpeed2);
1515  double nCurVel1 = linearMap(curVel1, minSpeed1, maxSpeed1, -1.0, 1.0);
1516  double nCurVel2 = linearMap(curVel2, minSpeed2, maxSpeed2, -1.0, 1.0);
1517  double nDesVel1 = linearMap(desVel1, minSpeed1, maxSpeed1, -1.0, 1.0);
1518  double nDesVel2 = linearMap(desVel2, minSpeed2, maxSpeed2, -1.0, 1.0);
1519  switch (m_mode) {
1520  case AbsoluteMode:
1521  m_neuronsIterator->setInput(applyNoise(nCurVel1, -1.0, 1.0));
1522  m_neuronsIterator->nextNeuron();
1523  m_neuronsIterator->setInput(applyNoise(nCurVel2, -1.0, 1.0));
1524  m_neuronsIterator->nextNeuron();
1525  break;
1526  case DeltaMode:
1527  m_neuronsIterator->setInput(applyNoise(min(1.0, fabs(nDesVel1 - nCurVel1)), 0.0, 1.0));
1528  m_neuronsIterator->nextNeuron();
1529  m_neuronsIterator->setInput(applyNoise(min(1.0, fabs(nDesVel2 - nCurVel2)), 0.0, 1.0));
1530  m_neuronsIterator->nextNeuron();
1531  break;
1532  default:
1533  // We should never get here
1534  throwUserRuntimeError("Invalid mode for MarXbotWheelSpeedsSensor");
1535  break;
1536  }
1537 }
1538 
1540 {
1541  return 2;
1542 }
1543 
1544 void MarXbotWheelSpeedsSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
1545 {
1546  // Calling parent function
1547  MarXbotSensor::resourceChanged(resourceName, changeType);
1548 
1549  if (changeType == Deleted) {
1550  return;
1551  }
1552 
1553  if (resourceName == m_marxbotResource) {
1554  m_robot = getResource<MarXbot>();
1555  } else if (resourceName == m_neuronsIteratorResource) {
1556  m_neuronsIterator = getResource<NeuronsIterator>();
1557  m_neuronsIterator->setCurrentBlock(name());
1558 
1559  // Creating labels
1560  real minNeuronValue = 0.0;
1561  real maxNeuronValue = 1.0;
1562  switch (m_mode) {
1563  case AbsoluteMode:
1564  minNeuronValue = -1.0;
1565  maxNeuronValue = 1.0;
1566  break;
1567  case DeltaMode:
1568  minNeuronValue = 0.0;
1569  maxNeuronValue = 1.0;
1570  break;
1571  default:
1572  // We should never get here
1573  minNeuronValue = 0.0;
1574  maxNeuronValue = 1.0;
1575  break;
1576  }
1577  m_neuronsIterator->setGraphicProperties("w0", minNeuronValue, maxNeuronValue, Qt::red);
1578  m_neuronsIterator->nextNeuron();
1579  m_neuronsIterator->setGraphicProperties("w1", minNeuronValue, maxNeuronValue, Qt::red);
1580  m_neuronsIterator->nextNeuron();
1581  } else {
1582  Logger::info("Unknown resource " + resourceName + " for " + name());
1583  }
1584 }
1585 
1587  MarXbotSensor(params, prefix),
1588  m_robot(NULL),
1589  m_neuronsIterator(NULL),
1590  m_sensorLeft(NULL),
1591  m_sensorCenter(NULL),
1592  m_sensorRight(NULL),
1593  m_maxDistance(ConfigurationHelper::getDouble(params, prefix + "maxDistance", 1.0)),
1594  m_aperture(ConfigurationHelper::getDouble(params, prefix + "aperture", 60.0)),
1595  m_drawSensors(ConfigurationHelper::getBool(params, prefix + "drawSensors", false)),
1596  m_drawRays(ConfigurationHelper::getBool(params, prefix + "drawRays", false)),
1597  m_drawRaysRange(ConfigurationHelper::getBool(params, prefix + "drawRaysRange", false))
1598 {
1599  // Sanity check
1600  if (m_maxDistance <= 0.0f) {
1601  ConfigurationHelper::throwUserConfigError(prefix + "maxDistance", params.getValue(prefix + "maxDistance"), "The parameter must be a positive real number");
1602  } else if ((m_aperture < 0.0f) || (m_aperture > 360.0f)) {
1603  ConfigurationHelper::throwUserConfigError(prefix + "aperture", params.getValue(prefix + "aperture"), "The parameter must be a positive real number smaller than 360");
1604  }
1605 }
1606 
1608 {
1609  delete m_sensorLeft;
1610  delete m_sensorCenter;
1611  delete m_sensorRight;
1612 }
1613 
1615 {
1616  // Calling parent function
1617  MarXbotSensor::save(params, prefix);
1618 
1619  // Saving parameters
1620  params.startObjectParameters(prefix, "MarXbotLaserFrontDistanceSensor", this);
1621  params.createParameter(prefix, "maxDistance", QString::number(m_maxDistance));
1622  params.createParameter(prefix, "aperture", QString::number(m_maxDistance));
1623  params.createParameter(prefix, "drawSensors", m_drawSensors ? QString("true") : QString("false"));
1624  params.createParameter(prefix, "drawRays", m_drawRays ? QString("true") : QString("false"));
1625  params.createParameter(prefix, "drawRaysRange", m_drawRaysRange ? QString("true") : QString("false"));
1626 }
1627 
1629 {
1630  // Calling parent function
1632 
1633  // Describing our parameters
1634  Descriptor d = addTypeDescription(type, "A frontal distance sensor for long distances", "This is a simple distance sensor in the frontal part of the robot with three rays, at -30°, 0° and 30° with respect to the frontal part of the robot. This could be implemented on the real robot using the laser scanner.");
1635  d.describeReal("maxDistance").def(1.0).limits(0.0f, +Infinity).help("The maximum distance of the sensors");
1636  d.describeReal("aperture").def(60.0).limits(0.0f, 360.0f).help("The sensor aperture in degrees");
1637  d.describeBool("drawSensors").def(false).help("Whether to draw the sensors or not");
1638  d.describeBool("drawRays").def(false).help("When drawing the sensor, whether to draw the rays or not");
1639  d.describeBool("drawRaysRange").def(false).help("When drawing the rays, whether to draw the real range or not");
1640 }
1641 
1643 {
1644  // Checking all resources we need exist
1646 
1647  // Acquiring the lock to get resources
1648  ResourcesLocker locker(this);
1649 
1650  // Setting the current block to update the input neurons
1651  m_neuronsIterator->setCurrentBlock(name());
1652 
1653  // Now reading values from the sensor
1654  m_sensorLeft->update();
1655  m_sensorCenter->update();
1656  m_sensorRight->update();
1657 
1658  m_neuronsIterator->setInput(applyNoise(1.0 - m_sensorLeft->getRayCastHit().distance, 0.0, 1.0));
1659  m_neuronsIterator->nextNeuron();
1660  m_neuronsIterator->setInput(applyNoise(1.0 - m_sensorCenter->getRayCastHit().distance, 0.0, 1.0));
1661  m_neuronsIterator->nextNeuron();
1662  m_neuronsIterator->setInput(applyNoise(1.0 - m_sensorRight->getRayCastHit().distance, 0.0, 1.0));
1663  m_neuronsIterator->nextNeuron();
1664 }
1665 
1667 {
1668  return 3;
1669 }
1670 
1671 void MarXbotLaserFrontDistanceSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
1672 {
1673  // Calling parent function
1674  MarXbotSensor::resourceChanged(resourceName, changeType);
1675 
1676  if (changeType == Deleted) {
1677  return;
1678  }
1679 
1680  if (resourceName == m_marxbotResource) {
1681  m_robot = getResource<MarXbot>();
1682 
1683  // Deleting the old sensors and creating a new ones
1684  delete m_sensorLeft;
1685  delete m_sensorCenter;
1686  delete m_sensorRight;
1687 
1688  wMatrix centralMtr = wMatrix::pitch(PI_GRECO / 2.0f);
1689  centralMtr.w_pos = wVector(0.0, -PhyMarXbot::bodyr, PhyMarXbot::basez + PhyMarXbot::trackradius * 2.0f + PhyMarXbot::treaddepth);
1690  m_sensorCenter = new SingleIR(m_robot, centralMtr, 0.02f, m_maxDistance, 0.0f, 1);
1691  m_sensorCenter->setGraphicalProperties(m_drawSensors, m_drawRays, m_drawRaysRange);
1692 
1693  wMatrix mtr = wMatrix::yaw(toRad(m_aperture / 2.0f)) * centralMtr;
1694  m_sensorLeft = new SingleIR(m_robot, mtr, 0.02f, m_maxDistance, 0.0f, 1);
1695  m_sensorLeft->setGraphicalProperties(m_drawSensors, m_drawRays, m_drawRaysRange);
1696 
1697  mtr = wMatrix::yaw(toRad(-m_aperture / 2.0f)) * centralMtr;
1698  m_sensorRight = new SingleIR(m_robot, mtr, 0.02f, m_maxDistance, 0.0f, 1);
1699  m_sensorRight->setGraphicalProperties(m_drawSensors, m_drawRays, m_drawRaysRange);
1700  } else if (resourceName == m_neuronsIteratorResource) {
1701  m_neuronsIterator = getResource<NeuronsIterator>();
1702  m_neuronsIterator->setCurrentBlock(name());
1703 
1704  // Creating labels
1705  m_neuronsIterator->setGraphicProperties("l0", 0.0, 1.0, Qt::red);
1706  m_neuronsIterator->nextNeuron();
1707  m_neuronsIterator->setGraphicProperties("l1", 0.0, 1.0, Qt::red);
1708  m_neuronsIterator->nextNeuron();
1709  m_neuronsIterator->setGraphicProperties("l2", 0.0, 1.0, Qt::red);
1710  m_neuronsIterator->nextNeuron();
1711  } else {
1712  Logger::info("Unknown resource " + resourceName + " for " + name());
1713  }
1714 }
1715 
1716 }