Given the supervisor and robot, a working controller can be set up. As described on the Robot page, this is achieved by implementing an actor, compliant with the RobotActor interface. Again, note that the interface is desined such that it may be matched by either a class or a function.

The actor gets the sensor measurements of the last epoch (i.e. since the previous execution of the actor) and information about the timeframe of the next action. Its purpose is to come up with a a sequence of motor targets that will be applied next. Read the reference of WebotsPuppyMixin carefully, so that you understand the setup and effect of the timings.

Although the task of implementing a controller is generally problem dependent, some typical actors have been prepared. The most common control setup is based on Gaits. A gait is a sinewave, expressed through its parameters (amplitude, frequency, phase and offset). Some parametersets that induce some specific walking patterns have already been found in various works. Yet, the gait itself does not implement a controller but only a series of motor targets. Besides that, the controller may also decide which gait to apply out of several choices. Or it might be desired to switch the gait during the experiment. Some actors have been prepared for this purpose, their reference and explanation can be found below.

One actor that deserves special attention is RobotCollector. It is a transparent actor, meaning that it delegates the action decision to another one but only stores the sensory data in a file (also see the Example).


This example is very similar to the one at Robot; make sure, you understand the robot aspects.

Again, it’s first required to import some modules:

>>> from controller import Robot
>>> import PuPy

In this example, the robot movement is based on some predefined walking patterns. A pattern is defined through a Gait, which generates a specific sinewave. The parameters of the wave have to be passed to the Gait constructor. Since there are four motors, there are four values for each parameter.

>>> boundLeft = PuPy.Gait({
>>>     'frequency' : (1.0, 1.0, 1.0, 1.0),
>>>     'offset'    : ( -0.23, -0.23, -0.37,-0.37),
>>>     'amplitude' : ( 0.38, 0.65, 0.47, 0.65),
>>>     'phase'     : (0.1141, 0.0, 0.611155, 0.5)
>>> })

One pattern would be quite boring, so another one is defined:

>>> boundRight = PuPy.Gait({
>>>     'frequency' : (1.0, 1.0, 1.0, 1.0),
>>>     'offset'    : ( -0.23, -0.23, -0.37, -0.37),
>>>     'amplitude' : ( 0.65, 0.38, 0.65, 0.47),
>>>     'phase'     : (0.0, 0.1141, 0.5, 0.611155)
>>> })

The gaits only implements a sequence of motor targets, not an actor. One might just apply one constant pattern (there’s ConstantGaitControl for that) but may also come up with other gait-based controllers. Here, the goal is to randomly switch between the two gaits. For this, the respective controller is initialized with the two gaits:

>>> actor = PuPy.RandomGaitControl([boundLeft, boundRight])

This now is an actor, as it implements the RobotActor interface - of course, RandomGaitControl is designed to do so. Yet, it might also be interesting to store sensor readouts of the simulation in a file for later inspection. For this purpose, there exists the RobotCollector. It is a transparent actor, meaning that it simulates an actor towards the WebotsPuppyMixin but lets a ‘true’ actor do the work. It just stores the provided measurements in a file.

This transparency can also be observed in the initialization, when the previously defined actor is passed as argument, together with a target file.

>>> observer = PuPy.RobotCollector(actor, expfile='/tmp/puppy_sim.hdf5')

When the actor is initialized, it is passed to the constructor of WebotsPuppyMixin through the robot builder. Then, the robot controller can be executed and its effect observed in webots or from the data log.

>>> r = PuPy.robotBuilder(Robot, observer, sampling_period_ms=20)

Once some data has been gathered and the simulation is stopped, the data file may be inspected. Note that the code below is not part of the control script anymore but a seperate python instance (e.g. in a script or interactively). Note that this piece of code employs [matplotlib] for plotting.

>>> import h5py, pylab
>>> f = h5py.File('/tmp/puppy_sim.hdf5','r')
>>> pylab.plot(f['0']['trg0'][:500], 'k', label='FL')
>>> pylab.plot(f['0']['trg2'][:500], 'b', label='HL')
>>> pylab.title('Motor targets')
>>> pylab.xlabel('time')
>>> pylab.legend(loc=0)

In the appearing window, the target of the front and hind left motor is displayed. It can be observed that the shape of the target curves change between the two gaits defined. The figure below gives an example of this.



class PuPy.RobotActor

Template class for an actor, used in WebotsPuppyMixin.

The actor is called after every control period, when a new sequence of motor targets is required. It is expected to return an iterator which in every step produces a 4-tuple, representing the targets of the motors. The order is front-left, front-right, rear-left, rear-right.


The sensor measurements in the previous control period. They are returned as dict, with the sensor name as key and a numpy array of observations as value.

Note that the motor targets are the ones that have been applied, i.e. those that lead up to the sensor measurements. Imagine this cycle:

trg[i] -> move robot -> sensors[i] -> trg[i+1] -> ...

Further, note that the dict() may be empty (this is guaranteed at least once in the simulator initialization).

The (simulated) time from which on the motor target will be applied. time_start_ms is weakly positive and strictly monotonic increasing (meaning that it is zero only in the very first call).
The (simulated) time up to which the motor target must at least be defined.
The motor period, i.e. the number of milliseconds pass until the next motor target is applied.

If the targets are represented by a list, it must at least have

(time_end_ms - time_start_ms) / step_size_ms

items and it has to be returned as iterator, as in

>>> iter(myList)
class PuPy.PuppyActor

Bases: PuPy.control.RobotActor

Deprecated alias for RobotActor.

class PuPy.Gait(params, name=None)

Motor target generator, using predefined gait_switcher.

The motor signal follows the parametrised sine

\(A \sin(2 \pi f x + p) + B\)

with the parameters A, f, p, B


dict() holding the parameters:

keys: amplitude, frequency, phase, offset

values: 4-tuples holding the parameter values. The order is
front-left, front-right, rear-left, rear-right
iter(time_start_ms, step)

Return the motor target sequence in the interval [time_start_ms, time_end_ms].

class PuPy.RandomGaitControl(gaits)

Bases: PuPy.control.PuppyActor

From a list of available gaits, randomly select one.

class PuPy.ConstantGaitControl(gait)

Bases: PuPy.control.PuppyActor

Given a gait, always apply it.

class PuPy.SequentialGaitControl(gait_iter)

Bases: PuPy.control.PuppyActor

Execute a predefined sequence of gaits.

Note that it’s assumed that gait_iter does not terminate permaturely.

class PuPy.RobotCollector(actor, expfile, headers=None)

Collect sensor readouts and store them in a file.

The data is stored in the [HDF5] format. For each simulation run, there’s a group, identified by a running number. Within each group, the sensor data is stored in exclusive datasets, placed under the sensor’s name.


This class abstracts interface from implementation. Internally, either the HDF5 interface from [PyTables] or [h5py] may be used.

The PuppyCollector works as intermediate actor, it does not implement a policy itself. For this, another actor is required. It must match the RobotActor interface.
Path to the file into which the experimental data should be stored.
Additional headers, stored with the current experiment. A dict is expected. Default is None (no headers).
class PuPy.PuppyCollector(actor, expfile, headers=None)

Bases: PuPy.control.RobotCollector

Deprecated alias for RobotCollector.

Table Of Contents

Previous topic


Next topic


This Page