evorobotexperiment.cpp
1 /********************************************************************************
2  * FARSA Experiments Library *
3  * Copyright (C) 2007-2012 *
4  * Stefano Nolfi <stefano.nolfi@istc.cnr.it> *
5  * Onofrio Gigliotta <onofrio.gigliotta@istc.cnr.it> *
6  * Gianluca Massera <emmegian@yahoo.it> *
7  * Tomassino Ferrauto <tomassino.ferrauto@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 "evorobotexperiment.h"
25 #include "sensors.h"
26 #include "motors.h"
27 #include "evoga.h"
28 #include "configurationhelper.h"
29 #include "factory.h"
30 #include "logger.h"
31 #include "simpletimer.h"
32 
33 #include <QFile>
34 #include <QTextStream>
35 #include <QString>
36 #include <QTime>
37 #include <QThread>
38 #include <iostream>
39 #include <cstdlib>
40 
41 // All the suff below is to avoid warnings on Windows about the use of unsafe
42 // functions. This should be only a temporary workaround, the solution is stop
43 // using C string and file functions...
44 #if defined(_MSC_VER)
45  #pragma warning(push)
46  #pragma warning(disable:4996)
47 #endif
48 
49 namespace farsa {
50 
51 /*
52  * Experiment constructor
53  */
55  : ntrials(1)
56  , nsteps(1)
57  , nstep(0)
58  , ntrial(0)
59  , savedConfigurationParameters(NULL)
60  , savedPrefix(NULL)
61  , ga(NULL)
62  , world(NULL)
63  , timestep(0.05f)
64  , eagents()
65  , agentIdSelected(0)
66  , gaPhase(NONE)
67  , stopCurrentTrial(false)
68  , skipCurrentTrial(false)
69  , restartCurrentTrial(false)
70  , endCurrentIndividualLife(false)
71  , batchRunning(false)
72  , arena(NULL)
73  , stepDelay(timestep*1000)
74  , sameRandomSequence(false)
75  , randomGeneratorInUse(farsa::globalRNG)
76  , localRNG(1)
77 {
78  // Stating which resources we use here. This is here in the constructor so that we are sure to
79  // be the first to declare resources (if we did this later we should have used addUsableResources
80  // because child classes could declare they will use resources before us)
81  usableResources( QStringList() << "world" << "arena" << "experiment" << "robot" << "evonet" << "neuronsIterator" );
82 }
83 
85 {
86  // Removing resources
87  try {
88  deleteResource("experiment");
89  deleteResource("arena");
90  deleteResource("world");
91  deleteResource("robot");
92  deleteResource("evonet");
93  deleteResource("neuronsIterator");
94  } catch (...) {
95  // Doing nothing, this is here just to prevent throwing an exception from the destructor
96  }
97 
98  foreach( EmbodiedAgent* agent, eagents ) {
99  delete agent;
100  }
101 
102  delete savedConfigurationParameters;
103  delete savedPrefix;
104  delete arena;
105  delete world;
106 }
107 
109 {
110  // Saving configuration parameters and prefix for cloning
111  delete savedConfigurationParameters;
112  delete savedPrefix;
113  savedConfigurationParameters = new ConfigurationParameters(params);
114  savedConfigurationParameters->shareObserversWith(params);
115  savedPrefix = new QString(prefix);
116  // Setting ourself as resource manager in the configuration parameters object
117  params.setResourcesUser(this);
118  savedConfigurationParameters->setResourcesUser(this);
119 
120  ntrials = 1;
121  nsteps = 1;
122 
123  batchRunning = ConfigurationHelper::getBool(params, "__INTERNAL__/BatchRunning", batchRunning); // If we are running in batch or not
124  ntrials = ConfigurationHelper::getInt(params, prefix + "ntrials", ntrials); // number of trials to do
125  notifyChangesToParam( "ntrials" );
126  nsteps = ConfigurationHelper::getInt(params, prefix + "nsteps", nsteps); // number of step for each trial
127 
128  // Reading world parameters. We need to do this before calling recreateWorld because that function uses the parameters
129  timestep = ConfigurationHelper::getDouble(params, prefix + "World/timestep", timestep);
130  // initializing the stepDelay at the same amount of timestep
131  // will slow down the simulation at real-time pace when the GUI is on
132  stepDelay = timestep*1000;
133 
134  // Getting the parameter that lets the user choose whether all individuals should use the same
135  // random sequence or not. We also set randomGeneratorInUse to point to the correct generator
136  sameRandomSequence = ConfigurationHelper::getBool(params, prefix + "sameRandomSequence", sameRandomSequence);
137  if (sameRandomSequence) {
138  randomGeneratorInUse = &localRNG;
139  } else {
140  randomGeneratorInUse = farsa::globalRNG;
141  }
142 
143  // create a World by default in order to exit from here with all configured properly
144  // if they are already created it will not destroy and recreate
145  recreateWorld();
146  // Creates the arena (if the group Arena is present)
147  recreateArena();
148  // Creates the Embodied Agents
149  // number of agents to create
150  int nagents = ConfigurationHelper::getInt(params, prefix + "nagents", 1);
151  if ( nagents > 1 ) {
152  // refactor the configuration parameters and create a subgroup foreach agent
153  for( int i=0; i<nagents; i++ ) {
154  QString agentPrefix = prefix + "AGENT:" + QString::number(i) + "/";
155  savedConfigurationParameters->createGroup( agentPrefix );
156  savedConfigurationParameters->copyGroupTree( prefix+"ROBOT", agentPrefix+"ROBOT" );
157  savedConfigurationParameters->copyGroupTree( prefix+"NET", agentPrefix+"NET" );
158  QStringList sensorsList = savedConfigurationParameters->getGroupsWithPrefixList(prefix, "Sensor:");
159  foreach( QString sensorGroup, sensorsList ) {
160  savedConfigurationParameters->copyGroupTree( prefix+sensorGroup, agentPrefix+sensorGroup );
161  }
162  QStringList motorsList = savedConfigurationParameters->getGroupsWithPrefixList(prefix, "Motor:");
163  foreach( QString motorGroup, motorsList ) {
164  savedConfigurationParameters->copyGroupTree( prefix+motorGroup, agentPrefix+motorGroup );
165  }
166  eagents.append( new EmbodiedAgent(i, agentPrefix, this) );
167  eagents.last()->configure();
168  }
169  } else {
170  eagents.append( new EmbodiedAgent(0, prefix, this) );
171  eagents.last()->configure();
172  }
173  selectAgent(0);
174 
175  // Adding robots to the arena (if the arena exists)
176  if (arena != NULL) {
177  QStringList robots;
178  foreach(EmbodiedAgent* e, eagents) {
179  robots.append(e->resourcePrefix+"robot");
180  }
181  arena->addRobots(robots);
182  }
183 
184  // declaring other resources
185  declareResource( "experiment", static_cast<ParameterSettableWithConfigureFunction*>(this) );
186 
187  Logger::info( params.getValue(prefix+"type") + " Configured" );
188 }
189 
191 {
192  Logger::error("NOT IMPLEMENTED (EvoRobotExperiment::save)");
193  abort();
194 }
195 
196 void EvoRobotExperiment::describe( QString type ) {
197  Descriptor d = addTypeDescription( type, "The experimental setup that defines the conditions and the fitness function of the evolutionary experiment" );
198  d.describeInt( "ntrials" ).def(1).limits(1,MaxInteger).runtime( &EvoRobotExperiment::setNTrials, &EvoRobotExperiment::getNTrials ).help("The number of trials the individual will be tested to calculate its fitness");
199  d.describeInt( "nsteps" ).def(1).limits(1,MaxInteger).help("The number of step a trials will last");
200  d.describeInt( "nagents" ).def(1).limits(1,MaxInteger).help("The number of embodied agents to create", "This parameter allow to setup experiments with more than one robot; all agents are clones");
201  d.describeBool("sameRandomSequence").def(false).help("Whether the generated random number sequence should be the same for all individuals in the same generation or not (default false)");
202  d.describeSubgroup( "NET" ).props( IsMandatory ).type( "Evonet" ).help( "The Neural Network controlling the robot");
203  d.describeSubgroup( "ROBOT" ).props( IsMandatory ).type( "Robot" ).help( "The robot");
204  d.describeSubgroup( "Sensor" ).props( AllowMultiple ).type( "Sensor" ).help( "One of the Sensors from which the neural network will receive information about the environment" );
205  d.describeSubgroup( "Motor" ).props( AllowMultiple ).type( "Motor" ).help( "One of the Motors with which the neural network acts on the robot and on the environment" );
206  d.describeSubgroup( "Arena" ).type( "Arena" ).help( "The arena where robots live");
207 
208  SubgroupDescriptor world = d.describeSubgroup( "World" ).help( "Parameters affecting the simulated World" );
209  world.describeReal( "timestep" ).def(0.05).runtime( &EvoRobotExperiment::setWorldTimestep, &EvoRobotExperiment::getWorldTimeStep ).help( "The time in seconds corresponding to one simulated step of the World" );
210 }
211 
213 {
214  if (!batchRunning) {
215  // preventing gas from using multithread, which is not supported if the GUI is present
217  }
218  // Doing evolution by default
219  gaPhase=EvoRobotExperiment::INEVOLUTION;
220 }
221 
222 void EvoRobotExperiment::doTrial()
223 {
224  restartCurrentTrial = false;
225  stopCurrentTrial = false;
226  trialFitnessValue = 0.0;
227  trialErrorValue = 0.0;
228  for(nstep = 0; nstep < nsteps; nstep++) {
229  initStep( nstep );
230  if ( ga->commitStep() || restartCurrentTrial ) {
231  break;
232  }
233  doStep();
234  if ( ga->commitStep() ) break;
235  endStep( nstep );
236  if (ga->commitStep() || stopCurrentTrial || restartCurrentTrial) {
237  break;
238  }
239  }
240 }
241 
243 {
244  // Checking if we have to reset the seed for the current individual
245  if (sameRandomSequence) {
246  localRNG.setSeed(ga->getCurrentSeed() + (ga->getCurrentGeneration() * ga->getNumReplications()));
247  }
248 
249  endCurrentIndividualLife = false;
250  totalFitnessValue = 0.0;
251 
252  initIndividual(individual);
253  if ( ga->commitStep() ) return;
254 
255  for (ntrial = 0; ntrial < ntrials; ntrial++) {
256  skipCurrentTrial = false;
257 
258  initTrial(ntrial);
259  if ( ga->commitStep() ) break;
260  if (skipCurrentTrial) { // && !ga->commitStep()) {
261  continue;
262  }
263  if (!endCurrentIndividualLife) {
264  doTrial();
265  }
266  if ( ga->isStopped() ) break;
267  if (restartCurrentTrial) {
268  ntrial--;
269  continue;
270  }
271  endTrial(ntrial);
272 
273  if (gaPhase == INTEST) {
274  Logger::info("Fitness for trial " + QString::number(ntrial) + ": " + QString::number(trialFitnessValue));
275  }
276 
277  if ( ga->commitStep() || endCurrentIndividualLife ) {
278  break;
279  }
280 
281  }
282 
283  endIndividual(individual);
284  ga->commitStep();
285 
286 #ifdef FARSA_MAC
287  // If the GUI is active, adding a small delay to let the GUI catch up (this is only done on MacOSX
288  // which seems the most troublesome system)
289  if (!batchRunning) {
290  // Using also the namespace to avoid name clashes
291  farsa::msleep(50);
292  }
293 #endif
294 }
295 
297 {
298 }
299 
301 {
302 }
303 
305 {
306  // reset the neural controller
307  ResourcesLocker locker(this);
308  foreach( EmbodiedAgent* agent, eagents ) {
309  agent->evonet->resetNet();
310  }
311 }
312 
314 {
315 }
316 
318 {
319  return totalFitnessValue;
320 }
321 
323 {
324  return totalErrorValue;
325 }
326 
328 {
329 }
330 
332 {
333 }
334 
336 {
337 }
338 
340 {
343 }
344 
346 {
347 }
348 
350 {
351 }
352 
353 void EvoRobotExperiment::doStep()
354 {
355  // There is no getResource below, but we are actually using resources so we must take the lock.
356  // We don't acquire the lock here, but lock and unlock when needed in the body of the function
357  ResourcesLocker locker(this, false);
358 
359  if (!batchRunning && stepDelay>0) {
360  // To use platform-independent sleep functions we have to do this...
361  class T : public QThread
362  {
363  public:
364  using QThread::sleep;
365  using QThread::msleep;
366  using QThread::usleep;
367  };
368  T::msleep( stepDelay );
369  }
370 
371  // update sensors
372  foreach( EmbodiedAgent* agent, eagents ) {
373  if (agent->enabled) {
374  for (int s = 0; s < agent->sensors.size(); s++) {
375  agent->sensors[s]->update();
376  }
377  }
378  }
380  // update the neural controller
381  locker.lock();
382  foreach( EmbodiedAgent* agent, eagents ) {
383  if (agent->enabled) {
384  agent->evonet->updateNet();
385  }
386  }
387  locker.unlock();
389  // setting motors
390  foreach( EmbodiedAgent* agent, eagents ) {
391  if (agent->enabled) {
392  for (int m = 0; m < agent->motors.size(); m++) {
393  agent->motors[m]->update();
394  }
395  }
396  }
398  // advance the world simulation
399  locker.lock();
400  if (arena != NULL) {
402  }
403  world->advance();
404  if (arena != NULL) {
406  }
407  locker.unlock();
408 }
409 
411 {
412  stopCurrentTrial = true;
413 }
414 
416 {
417  skipCurrentTrial = true;
418 }
419 
421 {
422  restartCurrentTrial = true;
423 }
424 
426 {
427  endCurrentIndividualLife = true;
428  stopCurrentTrial = true;
429 }
430 
432 {
433  ResourcesLocker locker(this);
434 
435  return eagents[0]->evonet->freeParameters();
436 }
437 
438 Sensor* EvoRobotExperiment::getSensor( QString name, int id ) {
439  if ( !eagents[id]->enabled ) {
440  Logger::error( "getSensor returned NULL pointer because the agent "+QString::number(id)+" is disabled" );
441  return NULL;
442  } else if ( eagents[id]->sensorsMap.contains( name ) ) {
443  return eagents[id]->sensorsMap[name];
444  } else {
445  Logger::error( "getSensor returned NULL pointer because there is no sensor named "+name+" in the agent "+QString::number(id) );
446  return NULL;
447  }
448 }
449 
450 Motor* EvoRobotExperiment::getMotor( QString name, int id ) {
451  if ( !eagents[id]->enabled ) {
452  Logger::error( "getMotor returned NULL pointer because the agent "+QString::number(id)+" is disabled" );
453  return NULL;
454  } else if ( eagents[id]->motorsMap.contains( name ) ) {
455  return eagents[id]->motorsMap[name];
456  } else {
457  Logger::error( "getMotor returned NULL pointer because there is no motor named "+name+" in the agent "+QString::number(id) );
458  return NULL;
459  }
460 }
461 
463 {
464  return batchRunning;
465 }
466 
468  return eagents.size();
469 }
470 
472  if (!eagents[id]->enabled) {
473  return false;
474  }
475  agentIdSelected = id;
476  declareResource( "robot",
477  eagents[agentIdSelected]->robot,
478  eagents[agentIdSelected]->resourcePrefix+"robot" );
479  declareResource( "evonet",
480  static_cast<farsa::ParameterSettable*>(eagents[agentIdSelected]->evonet),
481  eagents[agentIdSelected]->resourcePrefix+"evonet" );
482  declareResource( "neuronsIterator",
483  eagents[agentIdSelected]->neuronsIterator,
484  eagents[agentIdSelected]->resourcePrefix+"neuronsIterator" );
485  return true;
486 }
487 
488 void EvoRobotExperiment::enableAgent( int agentId ) {
489  eagents[agentId]->enable();
490 }
491 
492 void EvoRobotExperiment::disableAgent( int agentId ) {
493  eagents[agentId]->disable();
494 }
495 
496 bool EvoRobotExperiment::agentEnabled( int agentId ) {
497  return eagents[agentId]->enabled;
498 }
499 
501 {
502  return eagents[id]->evonet;
503 }
504 
506 {
507  this->ga = ga;
508  // Also resetting the seed of the local random number generator
509  localRNG.setSeed(ga->getCurrentSeed());
510 }
511 
513  return ga;
514 }
515 
517 {
518  ResourcesLocker locker(this);
519  foreach( EmbodiedAgent* agent, eagents ) {
520  agent->evonet->setParameters(genes);
521  }
522 }
523 
525 {
526  ResourcesLocker locker(this);
527  foreach( EmbodiedAgent* agent, eagents ) {
528  agent->evonet->setParameters(genes);
529  }
530 }
531 
533 {
534  Logger::error("EvoRobotExperiment::setTestingAgentAndSeed() not yet implemented");
535 }
536 
538  // Saving the old robot, the old arena and world to delete them after the new world has been
539  // created (as world is a resource, we need the old instance to exists during notifications.
540  // It can be safely deleted afterward)
541  World* const old_world = world;
542 
543  // TODO: parametrize the name and the dontUseYarp and all other parameters
544  world = new World( "World" );
545  world->setTimeStep( timestep );
546  world->setSize( wVector( -2.0f, -2.0f, -0.50f ), wVector( +2.0f, +2.0f, +2.0f ) );
547  world->setFrictionModel( "exact" );
548  world->setSolverModel( "exact" );
549  world->setMultiThread( 1 );
550  world->setIsRealTime( false );
551 
552  // Removing deleted resources (if they existed) and then re-declaring world
553  Arena* old_arena = arena;
554  if ( arena != NULL ) {
555  deleteResource( "arena" );
556  arena = NULL;
557  }
558 
559  QList<Robot*> old_robots;
560  if ( eagents.size() > 0 ) {
561  deleteResource( "robot" );
562  for( int i=0; i<eagents.size(); i++ ) {
563  if (eagents[i]->enabled) {
564  deleteResource( eagents[i]->resourcePrefix+"robot" );
565  old_robots.push_back(eagents[i]->robot);
566  eagents[i]->robot = NULL;
567  }
568  }
569  }
570 
571  declareResource( "world", world );
572 
573  // Now we can actually free memory
574  delete old_arena;
575  for (int i = 0; i < old_robots.size(); ++i) {
576  delete old_robots[i];
577  }
578  delete old_world;
579 }
580 
582  if (!eagents[id]->enabled) {
583  return false;
584  }
585  eagents[id]->recreateRobot();
586  if ( id == agentIdSelected ) {
587  // rebind the resource of the robot
588  declareResource( "robot",
589  eagents[agentIdSelected]->robot,
590  eagents[agentIdSelected]->resourcePrefix+"robot" );
591  }
592  return true;
593 }
594 
596  for (int i = 0; i < eagents.size(); i++ ) {
597  if (eagents[i]->enabled) {
598  recreateRobot(i);
599  }
600  }
601 }
602 
604  // First of all we need to check whether there is an Arena group or not
605  if (!ConfigurationHelper::hasGroup( *savedConfigurationParameters, (*savedPrefix) + "Arena" ) ) {
606  // This is just to be sure...
607  arena = NULL;
608  return;
609  }
610 
611  // to be sure that a World exist
612  if ( !world ) {
613  recreateWorld();
614  }
615 
616  // Taking lock because we need to use world
617  ResourcesLocker locker(this);
618  // Saving the old arena to delete it after the new arena has been created
619  Arena* const old_arena = arena;
620 
621  // Now creating the arena. We first set ourself as the resouce manager
622  savedConfigurationParameters->setResourcesUser(this);
623  arena = savedConfigurationParameters->getObjectFromGroup<Arena>((*savedPrefix) + "Arena");
624  arena->shareResourcesWith(this);
625  QStringList robots;
626  foreach(EmbodiedAgent* e, eagents) {
627  robots.append(e->resourcePrefix+"robot");
628  }
629  arena->addRobots(robots);
630 
631  // Unlocking before redeclaring the arena resource
632  locker.unlock();
633 
634  declareResource("arena", static_cast<Resource*>(arena), "world");
635  delete old_arena;
636 }
637 
639  eagents[id]->recreateNeuralNetwork();
640  if ( id == agentIdSelected ) {
641  // rebind the resource of the evonet
642  declareResource( "evonet",
643  static_cast<farsa::ParameterSettable*>(eagents[agentIdSelected]->evonet),
644  eagents[agentIdSelected]->resourcePrefix+"evonet" );
645  }
646 }
647 
649  for (int i = 0; i < eagents.size(); i++ ) {
651  }
652 }
653 
655 {
656  return randomGeneratorInUse;
657 }
658 
659 void EvoRobotExperiment::setWorldTimestep( float timestep ) {
660  ResourcesLocker locker(this);
661  this->timestep = timestep;
662  if ( !world ) return;
663  world->setTimeStep( timestep );
664 }
665 
667  return timestep;
668 }
669 
671 {
672  localRNG.setSeed(seed);
673 }
674 
676  stepDelay = delay;
677 }
678 
680  return stepDelay;
681 }
682 
683 EvoRobotExperiment::EmbodiedAgent::EmbodiedAgent( int id, QString agentPath, EvoRobotExperiment* exp ) {
684  this->id = id;
685  enabled = true;
686  this->agentPath = agentPath;
687  this->exp = exp;
688  evonet = NULL;
689  neuronsIterator = new EvonetIterator();
690  robot = NULL;
691  resourcePrefix = QString("agent[%1]:").arg(id);
692 }
693 
694 void EvoRobotExperiment::EmbodiedAgent::configure() {
695  exp->savedConfigurationParameters->setResourcesUser(exp);
696  // add to the experiment the resources will create here
697  exp->addUsableResource( resourcePrefix+"evonet" );
698  exp->addUsableResource( resourcePrefix+"robot" );
699  exp->addUsableResource( resourcePrefix+"neuronsIterator" );
700  recreateRobot();
701 
702  // Reading the sensors parameters. For each sensor there must be a subgroup Sensor:NN where NN is a progressive number
703  // (needed to specify the sensors order). Here we also actually create sensors
704  QStringList sensorsList = exp->savedConfigurationParameters->getGroupsWithPrefixList(agentPath, "Sensor:");
705  sensorsList.sort();
706  foreach( QString sensorGroup, sensorsList ) {
707  // Setting the prefix for resources that depend on the robot.
708  // !! WARNING !! this should only be a temporary implementation, a better implementation is needed
709  exp->savedConfigurationParameters->createParameter(
710  agentPath+sensorGroup, "__resourcePrefix__", resourcePrefix );
711  // !! END OF TRICK !!
712  Sensor* sensor = exp->savedConfigurationParameters->getObjectFromGroup<Sensor>(agentPath + sensorGroup);
713  if ( sensor == NULL ) {
714  Logger::error("Cannot create the Sensor from group " + *(exp->savedPrefix) + sensorGroup + ". Aborting");
715  abort();
716  }
717  // in order to avoid name clash when using more than one Sensor,
718  // the Sensors are renamed using the same name of the Group when they don't have a name assigned
719  if ( sensor->name() == QString("unnamed") ) {
720  sensor->setName( sensorGroup );
721  }
722  sensors.append( sensor );
723  sensors.last()->shareResourcesWith( exp );
724  Logger::info( "Created a Sensor named "+sensor->name() );
725  // if the user manually set the name and create a name clash, it is only reported as error in Logger
726  if ( sensorsMap.contains( sensor->name() ) ) {
727  Logger::error( "More than one sensor has name "+sensor->name()+" !! The name has to be unique !!" );
728  } else {
729  // add to the map
730  sensorsMap[sensor->name()] = sensor;
731  }
732  }
733 
734  // Now we do for motors what we did for sensors. Motor groups are in the form Motor:NN
735  QStringList motorsList = exp->savedConfigurationParameters->getGroupsWithPrefixList(agentPath, "Motor:");
736  motorsList.sort();
737  foreach( QString motorGroup, motorsList ) {
738  // Setting the prefix for resources that depend on the robot.
739  // !! WARNING !! this should only be a temporary implementation, a better implementation is needed
740  exp->savedConfigurationParameters->createParameter(
741  agentPath+motorGroup, "__resourcePrefix__", resourcePrefix );
742  // !! END OF TRICK !!
743  Motor* motor = exp->savedConfigurationParameters->getObjectFromGroup<Motor>(agentPath + motorGroup);
744  if (motor == NULL) {
745  Logger::error("Cannot create the Motor from group " + *(exp->savedPrefix) + motorGroup + ". Aborting");
746  abort();
747  }
748  // in order to avoid name clash when using more than one Motor,
749  // the Motors are renamed using the same name of the Group when they don't have a name assigned
750  if ( motor->name() == QString("unnamed") ) {
751  motor->setName( motorGroup );
752  }
753  motors.append( motor );
754  motors.last()->shareResourcesWith( exp );
755  Logger::info( "Created a Motor named "+motor->name() );
756  // if the user manually set the name and create a name clash, it is only reported as error in Logger
757  if ( motorsMap.contains( motor->name() ) ) {
758  Logger::error( "More than one motor has name "+motor->name()+" !! The name has to be unique !!" );
759  } else {
760  // add to the map
761  motorsMap[motor->name()] = motor;
762  }
763  }
764 
765  recreateNeuralNetwork();
766 
767  exp->declareResource( resourcePrefix+"neuronsIterator", neuronsIterator, resourcePrefix+"evonet" );
768 }
769 
770 EvoRobotExperiment::EmbodiedAgent::~EmbodiedAgent() {
771  delete robot;
772  delete evonet;
773  delete neuronsIterator;
774  for (int i = 0; i < sensors.size(); i++) {
775  delete sensors[i];
776  }
777  for (int i = 0; i < motors.size(); i++) {
778  delete motors[i];
779  }
780 }
781 
782 void EvoRobotExperiment::EmbodiedAgent::recreateRobot() {
783  // to be sure that a World exist
784  if ( !(exp->world) ) {
785  exp->recreateWorld();
786  }
787 
788  // Taking lock because we need to use world
789  ResourcesLocker locker(exp);
790  // Saving the old robot to delete it after the new robot has been created
791  Robot* const old_robot = robot;
792 
793  // Now creating the robot
794  exp->savedConfigurationParameters->setResourcesUser(exp);
795  robot = exp->savedConfigurationParameters->getObjectFromGroup<Robot>(agentPath + "ROBOT");
796 
797  // Unlocking before redeclaring the robot resource
798  locker.unlock();
799 
800  exp->declareResource( resourcePrefix+"robot", robot, "world" );
801  delete old_robot;
802 }
803 
804 void EvoRobotExperiment::EmbodiedAgent::recreateNeuralNetwork() {
805  // Saving the old evonet to delete it after the new evonet has been created
806  Evonet* const old_evonet = evonet;
807 
808  // Check the subgroup [NET]
809  if ( exp->savedConfigurationParameters->getValue( agentPath+"NET/netFile" ).isEmpty() ) {
810  // calculate the number of sensors and motors neurons
811  int nSensors = 0;
812  foreach( Sensor* sensor, sensors ) {
813  nSensors += sensor->size();
814  }
815  int nMotors = 0;
816  foreach( Motor* motor, motors ) {
817  nMotors += motor->size();
818  }
819  // it inject the calculated nSensor and nMotors
820  exp->savedConfigurationParameters->createParameter( agentPath+"NET", "nSensors", QString::number(nSensors) );
821  exp->savedConfigurationParameters->createParameter( agentPath+"NET", "nMotors", QString::number(nMotors) );
822  }
823  // Now creating the neural network. We first set ourself as the resouce manager, then we lock resources (because during configuration
824  // evonet could use resources, but the resource user it will use is not thread safe (SimpleResourceUser))
825  ResourcesLocker locker(exp);
826  exp->savedConfigurationParameters->setResourcesUser(exp);
827  evonet = exp->savedConfigurationParameters->getObjectFromGroup<Evonet>( agentPath+"NET" );
828  evonet->setNetworkName(QString::number(id));
829  locker.unlock();
830 
831  exp->declareResource( resourcePrefix+"evonet", static_cast<farsa::ParameterSettable*>(evonet) );
832 
833  // Here we have to take the lock again because we are going to change neuronsIterator
834  locker.lock();
835  delete old_evonet;
836  neuronsIterator->setEvonet( evonet );
837  // create the blocks associated to the network
838  int startIndex = 0;
839  foreach( Sensor* sensor, sensors ) {
840  neuronsIterator->defineBlock( sensor->name(), EvonetIterator::InputLayer, startIndex, sensor->size() );
841  startIndex += sensor->size();
842  }
843  startIndex = 0;
844  foreach( Motor* motor, motors ) {
845  neuronsIterator->defineBlock( motor->name(), EvonetIterator::OutputLayer, startIndex, motor->size() );
846  startIndex += motor->size();
847  }
848 }
849 
850 void EvoRobotExperiment::EmbodiedAgent::disable() {
851  if (enabled) {
852  enabled = false;
853 
854  // Deleting the resource for the robot and then robot
855  exp->deleteResource( resourcePrefix+"robot" );
856  delete robot;
857  robot = NULL;
858  }
859 }
860 
861 void EvoRobotExperiment::EmbodiedAgent::enable() {
862  if (!enabled) {
863  enabled = true;
864 
865  recreateRobot();
866  }
867 }
868 
869 } // end namespace farsa
870 
871 // All the suff below is to restore the warning state on Windows
872 #if defined(_MSC_VER)
873  #pragma warning(pop)
874 #endif
This class iterate over the neurons of a Evonet neural network.
void usableResources(QStringList resources)
double totalFitnessValue
the fitness value of the individual all over the trials
virtual void endTrial(int trial)
Called at the end of a trial.
virtual void initStep(int step)
Initialize whatever at step granularity when needed.
bool agentEnabled(int agentId)
Returns true if the agent is enabled, false otherwise.
bool selectAgent(int agentId)
select the i-the agent and make available the corresponding resources
void setFrictionModel(QString model)
RealDescriptor & runtime(void(T::*setter)(double), double(T::*getter)() const )
void skipTrial()
Skips the next trial.
void advance()
FARSA_UTIL_API RandomGenerator * globalRNG
void FARSA_UTIL_TEMPLATE msleep(unsigned int msec)
virtual void setNetParameters(float *genes)
Sets the free parameters of the neural network. This is done for all agents, even disabled ones...
void declareResource(QString name, T *resource, QString lockBuddy="")
void addRobots(QStringList robots)
Adds robots to the list of 2D objects.
Definition: arena.cpp:177
void setSize(const wVector &minPoint, const wVector &maxPoint)
void addUsableResource(QString resource)
void stopTrial()
Stops the current trial.
void restartTrial()
Stops the current trial and restarts it from scratch.
virtual void initGeneration(int generation)
Called at the beginning of a generation. This function is NEVER called concurrently on different obje...
bool copyGroupTree(QString sourceGroup, QString destGroup)
RealDescriptor describeReal(QString parameter)
void doAllTrialsForIndividual(int individual)
Performs all trials for the given individual.
bool isStopped()
return true if the evolution process has been stopped with stop()
Definition: evoga.cpp:3050
void recreateAllRobots()
Recreates all robots.
virtual void endStep(int step)=0
Called at the end of the step.
virtual ~EvoRobotExperiment()
Destructor.
int getNAgents()
return the number of agents present in the experiment
void setIsRealTime(bool b)
int getNTrials() const
Returns the number of trials.
virtual unsigned int getCurrentGeneration()
Returns the current generation.
Definition: evoga.cpp:3086
void setEvoga(Evoga *ga)
Set the Evoga on which this EvoRobotExperiment is used.
virtual void postConfigureInitialization()
This function is called after all linked objects have been configured.
The class modelling an arena.
Definition: arena.h:50
double totalErrorValue
the error of the individual all over the trials
Sensor * getSensor(QString sensorName, int agentId=0)
Return a pointer to the Sensor with name specified.
void recreateAllNeuralNetworks()
Recreates all neural networks.
virtual void shareResourcesWith(ResourcesUser *buddy)
Motor * getMotor(QString motorName, int agentId=0)
Return a pointer to the Motor with name specified.
int getStepDelay()
returns the current delay applied at each step
void recreateWorld()
current trial
void endIndividualLife()
Ends the invidual life.
RealDescriptor & help(QString shortHelp, QString longHelp=QString(""))
double getFitness()
return the current value of the fitness
virtual unsigned int getCurrentSeed()
Returns the current seed.
Definition: evoga.cpp:3096
The base abstract class for the Sensor hierarchy.
void setSeed(int seed)
void shareObserversWith(ConfigurationParameters &params)
static bool getBool(ConfigurationParameters &params, QString paramPath, bool def=false)
void createGroup(QString groupPath)
virtual void setTestingAgentAndSeed(int idindividual, int nreplica)
called by AbstractTest and subclasses to inform which individual is going to test ...
void enableAgent(int agentId)
Enables the i-th agent.
RandomGenerator * getRNG()
Returns the random generator.
static void info(QString msg)
bool commitStep()
this method marks the point on which it is called as a commited step of evolution ...
Definition: evoga.cpp:3040
static void error(QString msg)
void setTimeStep(real)
float getWorldTimeStep() const
helper method for getting timestep of the world at runtime
bool recreateRobot(int agentId=0)
Recreate the robot.
void recreateArena()
Recreates the arena.
Evonet * getNeuralNetwork(int agentId=0)
Return the neural network used for the experiment.
RealDescriptor & def(double defaultValue)
void recreateNeuralNetwork(int agentId=0)
Recreate the neural network.
void disableAgent(int agentId)
Disables the i-th agent.
Genetic algorithm from evorobot more or less (spare parts)
Definition: evoga.h:50
void notifyChangesToParam(QString paramName)
int getGenomeLength()
Returns the length of the genome.
double getError()
return the current value of the error
static void describe(QString type)
Add to Factory::typeDescriptions() the descriptions of all parameters and subgroups.
virtual void beforeWorldAdvance()
Called just before the world advances, after the update of motors.
virtual void endGeneration(int generation)
Called at the end of a generation. This function is NEVER called concurrently on different objects...
static double getDouble(ConfigurationParameters &params, QString paramPath, double def=0)
static Descriptor addTypeDescription(QString type, QString shortHelp, QString longHelp=QString(""))
QString getValue(QString path, bool alsoMatchParents=false) const
void newGASeed(int seed)
Called by the ga when the seed changes (e.g. in a new replication)
TypeToCreate * getObjectFromGroup(QString group, bool configure=true, bool forceObjectCreation=false)
void setMultiThread(int numThreads)
virtual void endIndividual(int individual)
Called at the end of an individual's life.
Evonet is the neural network taken from the EvoRobot.
Definition: evonet.h:121
QStringList getGroupsWithPrefixList(QString group, QString prefix) const
The base abstract class for the Motor hierarchy.
virtual void save(ConfigurationParameters &params, QString prefix)
Save the actual status of parameters into the ConfigurationParameters object passed.
virtual void doNotUseMultipleThreads()
Forces execution of the GA using no threads (i.e., in the current thread)
Definition: evoga.cpp:3170
void handleKinematicRobotCollisions()
Checks collisions of kinematic robots.
Definition: arena.cpp:348
bool inBatchRunning()
Return true if the simulation is running in batch modality, false if is running with the GUI...
int nstep
number of cycles
static const int MaxInteger
virtual void afterSensorsUpdate()
Called just after the updating of sensors and before the updating of the neural network.
void setResourcesUser(ResourcesUser *resourcesUser)
void setStepDelay(int delay)
set the delay to apply at each step for slowing down the simulation
virtual void initIndividual(int individual)
Called at the beginning of an individual's life.
virtual void beforeMotorsUpdate()
Called just before the updating of motors and after the updating of the neural network.
void deleteResource(QString name)
double trialFitnessValue
the fitness value of the individual during the execution of one trial
static int getInt(ConfigurationParameters &params, QString paramPath, int def=0)
double trialErrorValue
the error of the individual during the execution of one trial
virtual void initTrial(int trial)
void setNTrials(int new_ntrials)
Sets the number of trials.
The base common class that evaluate the fitness of a robot.
void setSolverModel(QString model)
void createParameter(QString groupPath, QString parameter)
Evoga * getEvoga()
Return the pointer to the Evoga setted.
virtual unsigned int getNumReplications()
Returns the number of replications.
Definition: evoga.cpp:3101
virtual void configure(ConfigurationParameters &params, QString prefix)
Configures the object using a ConfigurationParameters object.
static bool hasGroup(ConfigurationParameters &params, QString group)
void prepareToHandleKinematicRobotCollisions()
Call this before advancing the world to prepare stuffs needed by a call to handleKinematicRobotCollis...
Definition: arena.cpp:332