kheperasensors.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 "kheperasensors.h"
25 #include "configurationhelper.h"
26 #include "logger.h"
27 #include "graphicalwobject.h"
28 #include "arena.h"
29 
30 namespace farsa {
31 
33  Sensor(params, prefix),
34  m_kheperaResource("robot"),
35  m_neuronsIteratorResource("neuronsIterator")
36 {
37  // Reading parameters
40 
41  // Declaring the resources that are needed here
43 }
44 
46 {
47  // Nothing to do here
48 }
49 
50 void KheperaSensor::save(ConfigurationParameters& params, QString prefix)
51 {
52  // Calling parent function
53  Sensor::save(params, prefix);
54 
55  // Saving parameters
56  params.startObjectParameters(prefix, "KheperaSensor", this);
57  params.createParameter(prefix, "khepera", m_kheperaResource);
58  params.createParameter(prefix, "neuronsIterator", m_neuronsIteratorResource);
59 }
60 
61 void KheperaSensor::describe(QString type)
62 {
63  // Calling parent function
64  Sensor::describe(type);
65 
66  // Describing our parameters
67  Descriptor d = addTypeDescription(type, "The base class for Khepera sensors");
68  d.describeString("khepera").def("robot").help("the name of the resource associated with the khepera robot to use (default is \"robot\")");
69  d.describeString("neuronsIterator").def("neuronsIterator").help("the name of the resource associated with the neural network iterator (default is \"neuronsIterator\")");
70 }
71 
72 void KheperaSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
73 {
74  // Calling parent function
75  Sensor::resourceChanged(resourceName, changeType);
76 
77  // Here we only check whether the resource has been deleted and reset the check flag, the
78  // actual work is done in subclasses
79  if (changeType == Deleted) {
81  return;
82  }
83 }
84 
86  KheperaSensor(params, prefix),
87  m_robot(NULL),
88  m_neuronsIterator(NULL)
89 {
90 }
91 
93 {
94  // Nothing to do here
95 }
96 
98 {
99  // Calling parent function
100  KheperaSensor::save(params, prefix);
101 
102  // Saving parameters
103  params.startObjectParameters(prefix, "KheperaProximityIRSensor", this);
104 }
105 
107 {
108  // Calling parent function
110 
111  // Describing our parameters
112  Descriptor d = addTypeDescription(type, "The infrared proximity sensors of the Khepera robot", "The infrared proximity sensors of the Khepera II robot. These are the very short range IR sensors all around the base");
113 }
114 
116 {
117  // Checking all resources we need exist
119 
120  // Acquiring the lock to get resources
121  ResourcesLocker locker( this );
122 
123  m_neuronsIterator->setCurrentBlock(name());
124  for (int i = 0; i < size(); i++, m_neuronsIterator->nextNeuron()) {
125  m_neuronsIterator->setInput(m_robot->proximityIRSensorController()->activation(i));
126  }
127 }
128 
130 {
131  return 8;
132 }
133 
134 void KheperaProximityIRSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
135 {
136  // Calling parent function
137  KheperaSensor::resourceChanged(resourceName, changeType);
138 
139  if (changeType == Deleted) {
140  return;
141  }
142 
143  if (resourceName == m_kheperaResource) {
144  m_robot = getResource<PhyKhepera>();
145 
146  // Eanbling sensors
147  m_robot->proximityIRSensorController()->setEnabled(true);
148  } else if (resourceName == m_neuronsIteratorResource) {
149  m_neuronsIterator = getResource<NeuronsIterator>();
150  m_neuronsIterator->setCurrentBlock(name());
151  for (int i = 0; i < size(); i++, m_neuronsIterator->nextNeuron()) {
152  m_neuronsIterator->setGraphicProperties("ir" + QString::number(i), 0.0, 1.0, Qt::red);
153  }
154  } else {
155  Logger::info("Unknown resource " + resourceName + " for " + name());
156  }
157 }
158 
159 
161  KheperaSensor(params, prefix),
162  m_robot(NULL),
163  m_arena(NULL),
164  m_neuronsIterator(NULL),
165  m_activeSensors(ConfigurationHelper::getBoolVector(params, prefix + "activeSensors", "111111111111111111111111")),
166  m_numActiveSensors(m_activeSensors.count(true)),
167  m_roundSamples(ConfigurationHelper::getString(params, prefix + "roundSamples", "round.sam")),
168  m_smallSamples(ConfigurationHelper::getString(params, prefix + "smallSamples", "small.sam")),
169  m_wallSamples(ConfigurationHelper::getString(params, prefix + "wallSamples", "wall.sam"))
170 {
171  if (m_activeSensors.size() != 8) {
172  ConfigurationHelper::throwUserConfigError(prefix + "activeSensors", params.getValue(prefix + "activeSensors"), "The parameter must be a list of exactly 8 elements either equal to 1 or to 0 (do not use any space to separate elements, just put them directly one after the other)");
173  }
174 
175  // Checking that the sampled files have the right number of IR sensors
176  if (m_roundSamples.numIR() != 8) {
177  ConfigurationHelper::throwUserConfigError(prefix + "roundSamples", m_roundSamples.filename(), "The file has samples for the wrong number of sensors, expected 8, got " + QString::number(m_roundSamples.numIR()));
178  }
179  if (m_smallSamples.numIR() != 8) {
180  ConfigurationHelper::throwUserConfigError(prefix + "smallSamples", m_smallSamples.filename(), "The file has samples for the wrong number of sensors, expected 8, got " + QString::number(m_smallSamples.numIR()));
181  }
182  if (m_wallSamples.numIR() != 8) {
183  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()));
184  }
185 
186  // Here we also need the arena to work
187  addUsableResource("arena");
188 }
189 
191 {
192  // Nothing to do here
193 }
194 
196 {
197  // Calling parent function
198  KheperaSensor::save(params, prefix);
199 
200  // Saving parameters
201  params.startObjectParameters(prefix, "KheperaSampledProximityIRSensor", this);
202  QString activeSensorsString;
203  for (int i = 0; i < m_activeSensors.size(); i++) {
204  activeSensorsString += (m_activeSensors[i] ? "1" : "0");
205  }
206  params.createParameter(prefix, "activeSensors", activeSensorsString);
207  params.createParameter(prefix, "roundSamples", m_roundSamples.filename());
208  params.createParameter(prefix, "smallSamples", m_smallSamples.filename());
209  params.createParameter(prefix, "wallSamples", m_wallSamples.filename());
210 }
211 
213 {
214  // Calling parent function
216 
217  // Describing our parameters
218  Descriptor d = addTypeDescription(type, "The sampled proximity infrared sensors of the Khepera", "This is the sampled version of the proximity infrared sensors of the Khepera. This sensor only works with objects created using the Arena");
219  d.describeString("activeSensors").def("11111111").help("Which IR sensors of the robot are actually enabled", "This is a string of exactly 8 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)");
220  d.describeString("roundSamples").def("round.sam").help("The name of the file with samples for big round objects");
221  d.describeString("smallSamples").def("small.sam").help("The name of the file with samples for small round objects");
222  d.describeString("wallSamples").def("wall.sam").help("The name of the file with samples for walls");
223 }
224 
226 {
227  // Checking all resources we need exist
229 
230  // Acquiring the lock to get resources
231  ResourcesLocker locker(this);
232 
233  // Getting the list of objects in the arena
234  const QVector<PhyObject2DWrapper*>& objectsList = m_arena->getObjects();
235 
236  // Preparing the vector with activations and setting all values to 0
237  QVector<real> activations(m_activeSensors.size(), 0.0);
238 
239  // Cycling through the list of objects. We first need to get the current position and orientation of the robot
240  const wVector robotPos = m_robot->position();
241  const real robotAng = m_robot->orientation(m_arena->getPlane());
242  foreach(const PhyObject2DWrapper* obj, objectsList) {
243  // Computing angle and distance. We don't need to remove the robot to which this sensor belongs because
244  // the calculatations will give a negative distance
245  double distance;
246  double angle;
247 
248  // If computeDistanceAndOrientationFromRobot returns false, we have to discard this object
249  if (!obj->computeDistanceAndOrientationFromRobot(*(m_arena->getRobotWrapper(m_kheperaResource)), distance, angle)) {
250  continue;
251  }
252 
253  // Getting the activation. The switch is to understand which samples to use
254  QVector<unsigned int>::const_iterator actIt = QVector<unsigned int>::const_iterator();
255  switch (obj->type()) {
256  case PhyObject2DWrapper::Wall:
257  actIt = m_wallSamples.getActivation(distance, angle);
258  break;
259  case PhyObject2DWrapper::SmallCylinder:
260  actIt = m_smallSamples.getActivation(distance, angle);
261  break;
262  case PhyObject2DWrapper::BigCylinder:
263  case PhyObject2DWrapper::WheeledRobot:
264  actIt = m_roundSamples.getActivation(distance, angle);
265  break;
266  default:
267  Logger::warning("The sampled infrared sensor only works with Small Cylinders, Big Cylinders, Walls and other Robots");
268  continue;
269  }
270 
271  // Adding activations in the activations vector
272  for (int i = 0; i < activations.size(); ++i, ++actIt) {
273  activations[i] = min(1.0, activations[i] + (real(*actIt) / 1024.0));
274  }
275  }
276 
277  // Finally activating neurons
278  m_neuronsIterator->setCurrentBlock(name());
279  for (int i = 0; i < m_activeSensors.size(); i++) {
280  if (m_activeSensors[i]) {
281  m_neuronsIterator->setInput(activations[i]);
282  m_neuronsIterator->nextNeuron();
283  }
284  }
285 }
286 
288 {
289  return m_numActiveSensors;
290 }
291 
292 void KheperaSampledProximityIRSensor::resourceChanged(QString resourceName, ResourceChangeType changeType)
293 {
294  // Calling parent function
295  KheperaSensor::resourceChanged(resourceName, changeType);
296 
297  if (changeType == Deleted) {
298  return;
299  }
300 
301  if (resourceName == m_kheperaResource) {
302  m_robot = getResource<Khepera>();
303 
304  // Disabling proximity IR sensors, they are not used here
305  m_robot->proximityIRSensorController()->setEnabled(false);
306  } else if (resourceName == m_neuronsIteratorResource) {
307  m_neuronsIterator = getResource<NeuronsIterator>();
308  m_neuronsIterator->setCurrentBlock(name());
309 
310  // We use the actual sensor ID in the neuron name
311  for (int i = 0; i < m_activeSensors.size(); i++) {
312  if (m_activeSensors[i]) {
313  m_neuronsIterator->setGraphicProperties("ir" + QString::number(i), 0.0, 1.0, Qt::red);
314  m_neuronsIterator->nextNeuron();
315  }
316  }
317  } else if (resourceName == "arena") {
318  // Storing the pointer to the arena
319  m_arena = getResource<Arena>();
320  } else {
321  Logger::info("Unknown resource " + resourceName + " for " + name());
322  }
323 }
324 
325 }