If you are using RoboGen Online, the pre-compiled version for Windows, or you have built RoboGen with Qt enabled then there the possibility of writing your own scenario with a small amount of ECMAScript/JavaScript.

Writing a Scenario

In order to define a custom scenario (including a fitness function) you will implement a scenario class in ECMAScript (JavaScript). The one thing that you are required to do is to define a function getFitness.

For example, this would be a valid scenario file (but it will not be very useful, since every robot would get fitness 0).

{
   getFitness: function() {
   	return 0;
   },
}

Most likely you will want the fitness function to be based on what happens during the simulation. The important thing to understand is that since the getFitness function will be called after the simulation(s) are completed you will no longer have access to any information from the simulator at that time. So, in order to actually use information from the simulator you will need to implement additional methods. The methods you can optionally implement are

setupSimulation — called at the very start of each simulation

afterSimulationStep — called after every single step of a simulation

endSimulation — called at the end of each simulation

Each of these functions can do some internal processing, and should return true if there are no errors, or false if there is a fatal error and the program should exit.

As a simple example, say we want the fitness to be the distance that the robot’s core component moved (in two dimensions) during a single simulation. Our script file would then contain the following:

{
	setupSimulation: function() {
    		// record the starting position
    		this.startPos = this.getRobot().getCoreComponent().getRootPosition();
    		return true;
	},

	endSimulation: function() {
    		// find the distance between the starting position and ending position
    		var currentPos = this.getRobot().getCoreComponent().getRootPosition();
		var xDiff = (currentPos.x - this.startPos.x);
    		var yDiff = (currentPos.y - this.startPos.y);
    		this.fitness = Math.sqrt(Math.pow(xDiff,2) + Math.pow(yDiff,2));
    		return true;
	},

	getFitness: function() {
    		return this.fitness;
	},

}

More generally, you will want a scenario that can accommodate multiple simulations per fitness evaluation, i.e. to evolve robots that are robust to starting configurations and/or noise. In that case you will need to aggregate information from each simulation and then use this information to arrive at the ultimate fitness value.

The following shows how to implement our example “Racing Scenario” where the fitness in a single evaluation is the distance from the starting position to the closest part of the robot at the end of the simulation (to prevent just falling forward), and the ultimate fitness is the minimum across the evaluations (a robot is only as good as it is in its worst evaluation).

{
	// here we define a variable for record keeping
	distances : [],	
	// function called at the beginning of each simulation
	setupSimulation: function() {
		this.startPos = this.getRobot().getCoreComponent().getRootPosition();
		return true;
	},

	// function called at the end of each simulation
	endSimulation: function() {

		// Compute robot ending position from its closest part to the start pos
		var minDistance = Number.MAX_VALUE;
	    	
        	bodyParts = this.getRobot().getBodyParts();
  		console.log(bodyParts.length + " body parts");
		for (var i = 0; i < bodyParts.length; i++) {
  	  		var xDiff = (bodyParts[i].getRootPosition().x - this.startPos.x);
	  		var yDiff = (bodyParts[i].getRootPosition().y - this.startPos.y);
			var dist = Math.sqrt(Math.pow(xDiff,2) + Math.pow(yDiff,2));
            		if (dist < minDistance) {
				minDistance = dist;
			}
		}
	
		this.distances.push(minDistance);
		return true;
	},


	// here we return minimum distance travelled across evaluations
	getFitness: function() {
		fitness = this.distances[0];
    		for (var i = 1; i < this.distances.length; i++) {
			if (this.distances[i] < fitness)
				fitness = this.distances[i];
		}
     		return fitness;
	},

}

Stopping Simulations Early

Sometimes it might be useful to terminate a simulation early to save computational resources. This can be done by invoking this.stopSimulationNow() from within setupSimulation or afterSimulationStep.  If you do this then the simulation will stop before the next time step.

The following example shows how to skip the simulation (and return poor fitness) if the robot does not have any light sensors :

{
    // *** other initialization goes here *** //
    hasLightSensor : false,
    setupSimulation: function() {
        var sensors = this.getRobot().getSensors();
        // check if the robot has a light sensor
        for (var i=0; i<sensors.length; i++) {
            if (sensors[i].getType() == "LightSensor") {
                this.hasLightSensor = true;
            }
        }
        // if there is no light sensor then stop the simulator
        if (!this.hasLightSensor) {
            this.stopSimulationNow();
        }
        return true;
    },
    // *** rest of the scenario code goes here *** //
    getFitness: function() {
        // return bad fitness if no light sensor
        if (!this.hasLightSensor) {
            return -10000;
        }
        // *** normal fitness calculation goes here ** //
        return fitness;
    }
}

Important: you should still return true from setupSimulation and/or afterSimulationStep so that these function calls terminate successfully.

Note that after you do this, endSimulation and getFitness will still be called as normal (e.g. endSimulation will be called before the simulation objects are deleted).

Getting Information about Sensors

If you want to read sensor values in your scenario (e.g. to base your fitness function on them), you can get access to the sensors in two ways:

  1. By calling Robot.getSensors().  This will give you an array of all the sensors (if any) on the robot.
  2. By calling Model.getSensors().  This will give you an array of all the sensors (if any) on a specific body part.

Then, you can check which type of sensor the object represents by calling Sensor.getType() (See the Sensor documentation below).

Note: since the IMU actually has multiple values, the Sensor objects corresponding to the IMU will be of type ImuSensorElement.  In order to determine which DoF of the IMU this object represents you will need to look at its label (Sensor.getLabel()).  Specifically, you should check if the label ends with “x-acceleration”, “y-acceleration”,  “z-acceleration”, “Roll”, “Pitch”, or “Yaw”.  (Note: The Roll, Pitch and Yaw provided by Robogen are the angular velocities). This can be done as follows:

if (sensor.getType()=="ImuSensorElement") {
    var label = sensor.getLabel();
    if (/x-acceleration$/.test(label)) {
        // do something with x acceleration
    } else if (/y-acceleration$/.test(label)) {
        // do something with y acceleration
    } else if (/z-acceleration$/.test(label)) {
        // do something with z acceleration
    } else if (/Roll$/.test(label)) {
        // do something with roll
    } else if (/Pitch$/.test(label)) {
        // do something with pitch
    } else if (/Yaw$/.test(label)) {
        // do something with yaw
    }
}

 

Scenario API

A Scenario will have access to the following objects and methods

Scenario
Field/Method Type Description
getRobot() Robot Returns the robot
getEnvironment() Environment Returns the environment: i.e. all other objects in the Simulation besides the robot(s)
getCurTrial() int Returns the number of the current trial (starts with 0)
stopSimulationNow() void Call this if you want to stop the simulation immediately to save computational resources.  See Stopping Simulations Early.
vectorDistance(Vector3 vector1, Vector3 vector2) float Helper function to compute the distance between two 3D vectors (will also work for 2D vectors if no z component is given for either vector1 or vector2).

 

Robot
Field/Method Type Description
getCoreComponent() Model Returns the core component of the robot represented as a Model object.
getBodyParts() array<Model> Returns all of the body parts that make up the robot, each is represented as a Model object.
getMotors() array<Motor> Returns the motors that the robot has.
getSensors() array<Sensor> Returns the sensors that a robot has.

 

Model
Field/Method Type Description
getRootPosition() Vector3 Returns the position of the model’s root.
getRootAttitude() Quaternion Returns the attitude (orientation) of the model’s root.
getType() string Returns the type of the part in CamelCase. See http://robogen.org/docs/guidelines-for-writing-a-robot-text-file/
getSensors() array<Sensor> Returns the sensors on this body part.  If the part does not any sensors then this will be an empty array.

N.B. The component models are built up of several geometric primitives. The position / attitude of the “root” piece are made available.

Sensor
Field/Method Type Description
getLabel() string Returns the label (name) of the sensor.
getType() string Returns the type of the sensor: one of {ImuSensorElement, LightSensor, IrSensor, TouchSensor (deprecated) }. See Getting Information about Sensors above.
read() float Returns the sensor’s current value.

 

Motor
Field/Method Type Description
getId() IoPair Returns the id of the motor as an IoPair.
getVelocity() float Returns the motor’s current velocity.
getPosition() float Returns the motor’s current position.
getTorque() float Returns the motor’s current torque.

 

Environment
Field/Method Type Description
getLightSources() array<LightSource> Returns the light sources in the environment.
getAmbientLight() float Returns the ambient light present in the environment.
getObstacles() array<Obstacle> Returns the obstacles in the environment.

 

PositionObservable
Field/Method Type Description
getPosition() Vector3 Returns the object’s position.
getAttitude() Quaternion Returns the object’s attitude (orientation).

 

LightSource (extends PositionObservable)
Field/Method Type Description
getIntensity() float Returns the light source’s intensity.
setIntensity(float intensity) void Set the intensity of the light source.
setPosition(float x, float y, float z) void Set the position of the light source.

 

Obstacle (extends PositionObservable)

N.B. Currently all obstacles are BoxObstacles, but this may change in the future.

BoxObstacle (extends Obstacle)
Field/Method Type Description
getSize() Vector3 Return the box’s dimensions.

 

IoPair
Field/Method Type Description
partId string Id of the part that the sensor/motor is attached to.
ioId int ioIndex of the sensor/motor.

 

Vector3
Field/Method Type Description
x float x component of vector
y float y component of vector
z float z component of vector

 

Quaternion
Field/Method Type Description
x float x component of quat
y float y component of quat
z float z component of quat
w float w component of quat

Logging: it is possible to log to the browser’s JavaScript console by using console.log(). In the desktop version this will write to stdout.