marxbotmotors.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  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
21  ********************************************************************************/
22 
23 #include "marxbotmotors.h"
24 #include "configurationhelper.h"
25 #include "randomgenerator.h"
26 #include "logger.h"
27 
28 namespace farsa {
29 
31  Motor(params, prefix),
32  m_marxbotResource("robot"),
33  m_neuronsIteratorResource("neuronsIterator")
34 {
35  // Reading parameters
38 
39  // Declaring the resources that are needed here
41 }
42 
44 {
45  // Nothing to do here
46 }
47 
48 void MarXbotMotor::save(ConfigurationParameters& params, QString prefix)
49 {
50  // Calling parent function
51  Motor::save(params, prefix);
52 
53  // Saving parameters
54  params.startObjectParameters(prefix, "MarXbotMotor", this);
55  params.createParameter(prefix, "marxbot", m_marxbotResource);
56  params.createParameter(prefix, "neuronsIterator", m_neuronsIteratorResource);
57 }
58 
59 void MarXbotMotor::describe(QString type)
60 {
61  // Calling parent function
62  Motor::describe(type);
63 
64  // Describing our parameters
65  Descriptor d = addTypeDescription(type, "The base class for MarXbot motors");
66  d.describeString("marxbot").def("robot").help("The name of the resource associated with the MarXbot robot to use (default is \"robot\")");
67  d.describeString("neuronsIterator").def("neuronsIterator").help("The name of the resource associated with the neural network iterator (default is \"neuronsIterator\")");
68 }
69 
70 void MarXbotMotor::resourceChanged(QString resourceName, ResourceChangeType changeType)
71 {
72  // Calling parent function
73  Motor::resourceChanged(resourceName, changeType);
74 
75  // Here we only check whether the resource has been deleted and reset the check flag, the
76  // actual work is done in subclasses
77  if (changeType == Deleted) {
79  return;
80  }
81 }
82 
84  MarXbotMotor(params, prefix),
85  m_robot(NULL),
86  m_neuronsIterator(NULL)
87 {
88 }
89 
91 {
92  // Nothing to do here
93 }
94 
96 {
97  // Calling parent function
98  MarXbotMotor::save(params, prefix);
99 
100  // Saving parameters
101  params.startObjectParameters(prefix, "MarXbotWheelVelocityMotor", this);
102 }
103 
105 {
106  // Calling parent function
108 
109  // Describing our parameters
110  Descriptor d = addTypeDescription(type, "The motor controlling the velocity of the wheels of the MarXbot robot");
111 }
112 
114 {
115  // Checking all resources we need exist
117 
118  // Acquiring the lock to get resources
119  ResourcesLocker locker(this);
120 
121  // Getting limit velocities for wheels
122  double minSpeed1;
123  double minSpeed2;
124  double maxSpeed1;
125  double maxSpeed2;
126  m_robot->wheelsController()->getSpeedLimits(minSpeed1, minSpeed2, maxSpeed1, maxSpeed2);
127 
128  // Computing desired wheel velocities
129  m_neuronsIterator->setCurrentBlock(name());
130  const double v1 = (maxSpeed1 - minSpeed1) * m_neuronsIterator->getOutput() + minSpeed1;
131  m_neuronsIterator->nextNeuron();
132  const double v2 = (maxSpeed2 - minSpeed2) * m_neuronsIterator->getOutput() + minSpeed2;
133 
134  m_robot->wheelsController()->setSpeeds(v1, v2);
135 }
136 
138 {
139  return 2;
140 }
141 
142 void MarXbotWheelVelocityMotor::resourceChanged(QString resourceName, ResourceChangeType changeType)
143 {
144  // Calling parent function
145  MarXbotMotor::resourceChanged(resourceName, changeType);
146 
147  if (changeType == Deleted) {
148  return;
149  }
150 
151  if (resourceName == m_marxbotResource) {
152  m_robot = getResource<PhyMarXbot>();
153  } else if (resourceName == m_neuronsIteratorResource) {
154  m_neuronsIterator = getResource<NeuronsIterator>();
155  m_neuronsIterator->setCurrentBlock(name());
156  for (int i = 0; i < size(); i++, m_neuronsIterator->nextNeuron()) {
157  m_neuronsIterator->setGraphicProperties("W" + QString::number(i), 0.0, 1.0, Qt::red);
158  }
159  } else {
160  Logger::info("Unknown resource " + resourceName + " for " + name());
161  }
162 }
163 
165  MarXbotMotor(params, prefix),
166  m_robot(NULL),
167  m_neuronsIterator(NULL),
168  m_onColor(ConfigurationHelper::getString(params, prefix + "onColor", "+00FF00").replace("+", "#")), // We have to do this because # is for comments in .ini files
169  m_offColor(ConfigurationHelper::getString(params, prefix + "offColor", "+0000FF").replace("+", "#")), // We have to do this because # is for comments in .ini files
170  m_onThreshold(ConfigurationHelper::getDouble(params, prefix + "onThreshold", 0.8f)),
171  m_offThreshold(ConfigurationHelper::getDouble(params, prefix + "offThreshold", 0.2f))
172 {
173  if (m_onThreshold < m_offThreshold) {
174  ConfigurationHelper::throwUserConfigError(prefix + "offThreshold", params.getValue(prefix + "offThreshold"), "The offThreshold must be less than the onThreshold");
175  }
176  if (!m_onColor.isValid()) {
177  ConfigurationHelper::throwUserConfigError(prefix + "onColor", params.getValue(prefix + "onColor"), "The onColor value is not a valid color");
178  }
179  if (!m_offColor.isValid()) {
180  ConfigurationHelper::throwUserConfigError(prefix + "offColor", params.getValue(prefix + "offColor"), "The offColor value is not a valid color");
181  }
182 }
183 
185 {
186  // Nothing to do here
187 }
188 
190 {
191  // Calling parent function
192  MarXbotMotor::save(params, prefix);
193 
194  // Saving our parameters
195  params.startObjectParameters(prefix, "MarXbotColorMotor", this);
196  params.createParameter(prefix, "onColor", m_onColor.name().replace("#", "+"));
197  params.createParameter(prefix, "offColor", m_offColor.name().replace("#", "+"));
198  params.createParameter(prefix, "onThreshold", QString::number(m_onThreshold));
199  params.createParameter(prefix, "offThreshold", QString::number(m_offThreshold));
200 }
201 
202 void MarXbotColorMotor::describe(QString type)
203 {
204  // Calling parent function
206 
207  Descriptor d = addTypeDescription(type, "The MarXbot motor changing the robot color");
208  d.describeString("onColor").def("+00FF00").help("The \"on\" color of the robot.", "This is a string. Its format can be: +RGB, +RRGGBB, +RRRGGGBBB, +RRRRGGGGBBBB (each of R, G, and B is a single hex digit) or a name from the list of colors defined in the list of SVG color keyword names provided by the World Wide Web Consortium (see http://www.w3.org/TR/SVG/types.html#ColorKeywords). The default value is \"+00FF00\"");
209  d.describeString("offColor").def("+0000FF").help("The \"off\" color of the robot.", "This is a string. Its format can be: +RGB, +RRGGBB, +RRRGGGBBB, +RRRRGGGGBBBB (each of R, G, and B is a single hex digit) or a name from the list of colors defined in the list of SVG color keyword names provided by the World Wide Web Consortium (see http://www.w3.org/TR/SVG/types.html#ColorKeywords). The default value is \"+0000FF\"");
210  d.describeReal("onThreshold").def(0.8f).limits(0.0f, 1.0f).help("The value above which the color is switched to the \"on\" color", "This is the threshold above which the color of the robot is set to the one defined by the onColor parameter. The default value is 0.8");
211  d.describeReal("offThreshold").def(0.2f).limits(0.0f, 1.0f).help("The value below which the color is switched to the \"off\" color", "This is the threshold below which the color of the robot is set to the one defined by the offColor parameter. The default value is 0.2");
212 }
213 
215 {
216  // Checking all resources we need exist
218 
219  // Acquiring the lock to get resources
220  ResourcesLocker locker(this);
221 
222  // Getting the value of the output neuron
223  m_neuronsIterator->setCurrentBlock(name());
224  const real output = m_neuronsIterator->getOutput();
225 
226  // Setting the color of the robot
227  if (output > m_onThreshold) {
228  m_robot->setColor(m_onColor);
229  } else if (output < m_offThreshold) {
230  m_robot->setColor(m_offColor);
231  }
232 }
233 
235 {
236  return 1;
237 }
238 
239 void MarXbotColorMotor::resourceChanged(QString resourceName, ResourceChangeType changeType)
240 {
241  // Calling parent function
242  MarXbotMotor::resourceChanged(resourceName, changeType);
243 
244  if (changeType == Deleted) {
245  return;
246  }
247 
248  if (resourceName == m_marxbotResource) {
249  m_robot = getResource<PhyMarXbot>();
250 
251  // Setting color to the off color
252  m_robot->setColor(m_offColor);
253  } else if (resourceName == m_neuronsIteratorResource) {
254  m_neuronsIterator = getResource<NeuronsIterator>();
255  m_neuronsIterator->setCurrentBlock(name());
256  m_neuronsIterator->setGraphicProperties("C", 0.0, 1.0, Qt::red);
257  } else {
258  Logger::info("Unknown resource " + resourceName + " for " + name());
259  }
260 }
261 
263  MarXbotMotor(params, prefix),
264  m_robot(NULL),
265  m_neuronsIterator(NULL),
266  m_typeOfClosure(typeOfClosureFromString(ConfigurationHelper::getString(params, prefix + "typeOfClosure", "onlyClose"))),
267  m_noMotionRange(ConfigurationHelper::getDouble(params, prefix + "noMotionRange", 0.3f)),
268  m_noMotionRangeLow(0.5 - (m_noMotionRange / 2.0)),
269  m_noMotionRangeUp(0.5 + (m_noMotionRange / 2.0))
270 {
271  if (m_typeOfClosure == Invalid) {
272  ConfigurationHelper::throwUserConfigError(prefix + "typeOfClosure", params.getValue(prefix + "typeOfClosure"), "The typeOfClosure value is not valid");
273  }
274  if ((m_noMotionRange < 0.0) || (m_noMotionRange > 1.0)) {
275  ConfigurationHelper::throwUserConfigError(prefix + "noMotionRange", params.getValue(prefix + "noMotionRange"), "The noMotionRange value must be between 0.0 and 1.0");
276  }
277 }
278 
280 {
281  // Nothing to do here
282 }
283 
285 {
286  // Calling parent function
287  MarXbotMotor::save(params, prefix);
288 
289  // Saving our parameters
290  params.startObjectParameters(prefix, "MarXbotAttachmentDeviceMotor", this);
291  params.createParameter(prefix, "typeOfClosure", typeOfClosureToString(m_typeOfClosure));
292  params.createParameter(prefix, "noMotionRange", QString::number(m_noMotionRange));
293 }
294 
296 {
297  // Calling parent function
299 
300  Descriptor d = addTypeDescription(type, "The motor controlling the attachment device of the MarXbot");
301  d.describeEnum("typeOfClosure").def("onlyClose").values(QStringList() << "onlyClose" << "onlyHalfClose" << "both").help("How the attachment device is closed", "this parameter controls how the attachement device is closed. Possible values are: \"onlyClose\", \"onlyHalfClose\" and \"both\" (default is \"onlyClose\")");
302  d.describeReal("noMotionRange").def(0.3f).limits(0.0f, 1.0f).help("The dimension of the range corresponding to no movement", "This parameter defines how big is the range of activation which corresponds to no movement of the attachement device. If, for example, the value is 0.4, it means that activations from 0.3 to 0.7 cause no movement of the attachment device (default is 0.3)");
303 }
304 
306 {
307  // Checking all resources we need exist
309 
310  // Acquiring the lock to get resources
311  ResourcesLocker locker(this);
312 
313  // Getting the value of the output neurons
314  m_neuronsIterator->setCurrentBlock(name());
315  const real outVel = m_neuronsIterator->getOutput();
316  m_neuronsIterator->nextNeuron();
317  const real outStatus = m_neuronsIterator->getOutput();
318 
319  // Getting the controller for the attachment device
321 
322  // First computing the actual velocity
323  real velocity;
324  if (outVel < m_noMotionRangeLow) {
325  velocity = ((outVel - m_noMotionRangeLow) / m_noMotionRangeLow) * ctrl->getMaxVelocity();
326  } else if (outVel > m_noMotionRangeUp) {
327  velocity = ((outVel - m_noMotionRangeUp) / (1.0 - m_noMotionRangeUp)) * ctrl->getMaxVelocity();
328  } else {
329  velocity = 0;
330  }
331  ctrl->setDesiredVelocity(velocity);
332 
333  // Now computing the new desired status of the attachment device
334  MarXbotAttachmentDeviceMotorController::Status status = MarXbotAttachmentDeviceMotorController::Open;
335  switch (m_typeOfClosure) {
336  case OnlyClose:
337  if (outStatus > 0.5) {
338  status = MarXbotAttachmentDeviceMotorController::Closed;
339  }
340  break;
341  case OnlyHalfClose:
342  if (outStatus > 0.5) {
343  status = MarXbotAttachmentDeviceMotorController::HalfClosed;
344  }
345  break;
346  case Both:
347  if (outStatus > 0.66) {
348  status = MarXbotAttachmentDeviceMotorController::Closed;
349  } else if (outStatus > 0.33) {
350  status = MarXbotAttachmentDeviceMotorController::HalfClosed;
351  }
352  break;
353  }
354  ctrl->setDesiredStatus(status);
355 }
356 
358 {
359  return 2;
360 }
361 
362 void MarXbotAttachmentDeviceMotor::resourceChanged(QString resourceName, ResourceChangeType changeType)
363 {
364  // Calling parent function
365  MarXbotMotor::resourceChanged(resourceName, changeType);
366 
367  if (changeType == Deleted) {
368  return;
369  }
370 
371  if (resourceName == m_marxbotResource) {
372  m_robot = getResource<PhyMarXbot>();
373 
374  // Enabling the attachment device motor controller
375  m_robot->enableAttachmentDevice(true);
376  m_robot->attachmentDeviceController()->setEnabled(true);
377  } else if (resourceName == m_neuronsIteratorResource) {
378  m_neuronsIterator = getResource<NeuronsIterator>();
379  m_neuronsIterator->setCurrentBlock(name());
380 
381  m_neuronsIterator->setGraphicProperties("AV", 0.0, 1.0, Qt::red);
382  m_neuronsIterator->nextNeuron();
383  m_neuronsIterator->setGraphicProperties("AC", 0.0, 1.0, Qt::red);
384  } else {
385  Logger::info("Unknown resource " + resourceName + " for " + name());
386  }
387 }
388 
389 MarXbotAttachmentDeviceMotor::TypeOfClosure MarXbotAttachmentDeviceMotor::typeOfClosureFromString(QString value)
390 {
391  value = value.toUpper();
392 
393  if (value == "ONLYCLOSE") {
394  return OnlyClose;
395  } else if (value == "ONLYHALFCLOSE") {
396  return OnlyHalfClose;
397  } else if (value == "BOTH") {
398  return Both;
399  } else {
400  return Invalid;
401  }
402 }
403 
404 QString MarXbotAttachmentDeviceMotor::typeOfClosureToString(MarXbotAttachmentDeviceMotor::TypeOfClosure value)
405 {
406  switch(value) {
407  case OnlyClose:
408  return "OnlyClose";
409  case OnlyHalfClose:
410  return "OnlyHalfClose";
411  case Both:
412  return "Both";
413  default:
414  return "Invalid";
415  }
416 }
417 
418 }