Webots ecosystem

Introduction

The [Webots] simulator already offers Python bindings. The reference manual can be found here.

Besides the models of the environment and of course the robot, Webots requires two controllers which implement the robot and simulation logic. The robot model is not part of this library but assumed to be available. The focus of this module lies on the controllers. The robot controller implements the robot’s logic. If there’s no such controller, the robot will just remain on its place. It’s the controller’s responsibility to define motor targets (possibly with respect to sensory readouts) on behalf of which the simulator will act. The Supervisor controller offers a global view (thus it may access some information, the robot may not) and allows to start, restart, revert or quit the simulation. As hinted, it is used to supervise the experiment. Both controllers have to be implemented in a seperate file and correctly linked to a webots world (see Supporting material) in order to be executed.

Warning

When running, the Supervisor and Robot controllers are executed as seperate processes. When the simulation is reverted (or quit, naturally), both processes are terminated. This introduces all the common issues with concurrency and inter process communication. Keep this in mind and consider restarting instead of reverting a simulation. Also make use of the intrinsic communication channels.

Webots offers Python bindings, but the respective module is only available within a Webots execution. Since we don’t want to force this module to be within Pythons default module search path, the PuPy module must not directly depend on it. Due to this, a bit more complex approach must be taken. The library offers two classes (WebotsPuppyMixin, WebotsSupervisorMixin) which have to be mixed into Webot’s Robot and Supervisor class at runtime. To do so more easily, a builder is offered for both (robotBuilder(), supervisorBuilder()).

For example, in the controller script, setting up a working instance is as easy as this:

>>> from controller import Robot
>>> import PuPy
>>> robot = PuPy.robotBuilder(Robot, <args>)

with <args> the arguments to WebotsPuppyMixin. The example works analogous for the Supervisor. If you subclass WebotsPuppyMixin or WebotsSupervisorMixin, the working instance can be set up through the builder() function.

Reference

PuPy.mixin(cls_mixin, cls_base)

Create a class which derives from two base classes.

The intention of this function is to set up a working webots controller, using webots’s Robot class and the Puppy mixin from this library.

mixin
The mixin class, e.g. WebotsPuppyMixin or WebotsSupervisorMixin
base
The base class, e.g. Robot or Supervisor
PuPy.builder(cls_mixin, cls_base, *args, **kwargs)

Set up a mixin class and return an instance of it.

PuPy.robotBuilder(cls_base, *args, **kwargs)

Return an instance of a webots puppy robot.

PuPy.supervisorBuilder(cls_base, *args, **kwargs)

Return an instance of a webots puppy supervisor.

Supporting material

When experimenting with several controllers and environments, webots requires a world file (and some others) for each configuration. Usually, these differ only in some aspects while most of the content is identical. Also, the controller is part of the configuration, making it very annoying to keep track of subtle differences and working versions. To support development, two small scripts are provided which take care of this problem.

Splitter

Starting with a world (including the robot and environment), it is first split into its parts and each one stored with a certain name. This way, information about the robot or world can be extracted and later identified. For example, you can store several versions of the robot without redundant data. This script extracts the desired information from the world file and stores it in an own data structure. Specify what parts should be saved and under what name.

Note

The script requires the robot to be named puppy.

#!/bin/bash

# TODO: Copy stuff like textures, prototypes, ...

usage()
{
cat << EOF
usage: $0 options <Input world file>

   <DESC>

OPTIONS:
   -a      Save terrain, puppy, supervisor (give name)
   -t      Save terrain (give name)
   -p      Save puppy (give name)
   -s      Save supervisor (give name)
   -c      Save controllers (give path)
   -b      Target base directory (give path)
   -h      Show this message
EOF
}

SAVECONTROLLER=0
BASE="/home/matthias/studium/master/work/webots/data"
INPUT=
TERRAIN=
PUPPY=
SUPERVISOR=
CTRL=

while getopts “a:t:p:s:c:b:h” OPTION
do
     case $OPTION in
         a)
             TERRAIN=$OPTARG
             PUPPY=$OPTARG
             SUPERVISOR=$OPTARG
             ;;
         t)
             TERRAIN=$OPTARG
             ;;
         p)
             PUPPY=$OPTARG
             ;;
         s)
             SUPERVISOR=$OPTARG
             ;;
         c)
             CTRL=$OPTARG
             ;;
         b)
             BASE=$OPTARG
             ;;
         h)
             usage
             exit 1
             ;;
         ?)
             echo "?"
             usage
             exit
             ;;
     esac
done

shift $((OPTIND-1))
INPUT=$@

# check input
if [[ ! -e $INPUT ]] || [[ -z $BASE ]]
then
     usage
     exit 1
fi

# analyze file
START_PUPPY=`cat $INPUT | grep -n '^DEF puppy Robot' | awk '{split($0,a,":"); print a[1]}'`
START_SUPER=`cat $INPUT | grep -n '^Supervisor' | awk '{split($0,a,":"); print a[1]}'`
LINES=`wc -l $INPUT | awk '{split($0,a); print a[1]}'`

if [ $START_PUPPY -gt $START_SUPER ]; then
    echo "Cannot split the world file; Please reformat"
    exit 1
fi

HEAD=`head -n $[$START_PUPPY-1] $INPUT`
ROBOT=`head -n $[$START_SUPER-1] $INPUT | tail -n $[$START_SUPER - $START_PUPPY]`
SUPER=`tail -n $[$LINES-$START_SUPER+1] $INPUT`

mkdir -p $BASE/terrain $BASE/puppy $BASE/supervisor

if [[ ! -z $TERRAIN ]]; then echo "$HEAD" > $BASE/terrain/$TERRAIN; fi
if [[ ! -z $PUPPY ]]; then echo "$ROBOT" > $BASE/puppy/$PUPPY; fi
if [[ ! -z $SUPERVISOR ]]; then echo "$SUPER" > $BASE/supervisor/$SUPERVISOR; fi

if [[ $CTRL ]]
then
    PCTRL=`echo "$ROBOT" | grep 'controller' | sed 's/^.*controller\s\+"\(.*\)"/\1/'`
    SCTRL=`echo "$SUPER" | grep 'controller' | sed 's/^.*controller\s\+"\(.*\)"/\1/'`
    
    PTH=`readlink -f $INPUT`
    PTH=`dirname $PTH`
    mkdir -p $CTRL
    cp $PTH/../controllers/$PCTRL/$PCTRL.* $CTRL/puppy
    cp $PTH/../controllers/$SCTRL/$SCTRL.* $CTRL/supervisor
    
    # get the filename extension of the scripts
    #filename=$(basename "$fullfile")
    #extension="${filename##*.}"
    #filename="${filename%.*}"
fi

Builder

You have to define a robot and supervisor controller and choose an environment. The script will set up a simulator world from these arguments and start webots. This way, controllers (and environments) may be mixed quickly without having several files with almost the same content. The controller scripts are symlinked, so they can be edited while the world is active in webots. Naturally, the changes take effect after reverting the simulation.

Note

This script is written for common Linux systems (it requires bash and symlinks). Other plattforms may not be supported.

Note

The supplementary files (terrain, plugins, prototypes, etc.) must be installed in the same location as the script itself. If this behaviour is not desired, set the $BASE variable to the correct directory. You can generate some of these files with the Splitter script above. Also check out the Downloads section.

Note

If you wish to create a snapshot of an active controller, the Splitter script may be useful. It copies the controllers to some location, usually outside of your common working directory.

#!/bin/bash

usage()
{
cat << EOF
usage: $0 -c <puppy controller> -s <supervisor controller> [optional options] <output>

   Note: It may be convenient to set the default value of -b in
         this script manually (default is script location)

OPTIONS:
   -c      Puppy controller
   -s      Supervisor controller
   -t      Terrain
   -p      Puppy
   -f      Overwrite target
   -b      Directory of your webots data
   -m      starting mode of webots: stop, realtime (default), run or fast
   -h      Show this message
EOF
}

WEBOTS="/usr/local/bin/webots"
BASE="$(dirname "$(readlink "${BASH_SOURCE[0]}")")/data"

TRG=

PCTRL=
SCTRL=
WORLD="default"
PUPPY="default"
SUPERVISOR="default"
MODE="stop"
while getopts “hp:s:t:c:b:m:” OPTION
do
     case $OPTION in
         h)
             usage
             exit 1
             ;;
         p)
             PUPPY=$OPTARG
             ;;
         t)
             WORLD=$OPTARG
             ;;
         c)
             PCTRL=$OPTARG
             ;;
         s)
             SCTRL=$OPTARG
             ;;
         b)
             BASE=$OPTARG
             ;;
         m)
             MODE=$OPTARG
             ;;
         ?)
             usage
             exit
             ;;
     esac
done

shift $((OPTIND-1))

# target
if [[ -z $@ ]]; then
    usage
    exit 1
fi

TRG=$@

if [[ -e $TRG ]]; then
    echo "WARNING: Target exists. Overwriting"
    rm -R $TRG
fi

# check input files
if [[ -z $PCTRL ]] || [[ -z $SCTRL ]] || [[ -z $TRG ]]
then
     usage
     exit 1
fi

# check working directory
if [[ ! -e $BASE/terrain ]] || [[ ! -e $BASE/puppy ]] || [[ ! -e $BASE/supervisor ]] || [[ ! -e $BASE/supplementary ]]
then
    echo "Base directory seems not to fit. Please change your working directory"
    echo "The current base is $BASE"
    usage
    exit 1
fi

# check other input files
if [[ ! -e $BASE/terrain/$WORLD ]]; then echo "WORLD not found"; exit 1; fi
if [[ ! -e $BASE/puppy/$PUPPY ]]; then echo "PUPPY not found"; exit 1; fi
if [[ ! -e $BASE/supervisor/$SUPERVISOR ]]; then echo "SUPERVISOR not found"; exit 1; fi

# check mode argument
if [[ ! $MODE == "stop" ]] && [[ ! $MODE == "realtime" ]] && [[ ! $MODE == "run" ]] && [[ ! $MODE == "fast" ]]
then
    echo "The webots run mode is $MODE, but must be one of (stop, realtime, run, fast)."
    exit 1
fi

# create data structure
mkdir -p $TRG
cp -R $BASE/supplementary/* $TRG
mkdir -p $TRG/controllers/

# set up world file
cat $BASE/terrain/$WORLD > $TRG/worlds/puppy_world.wbt
cat $BASE/puppy/$PUPPY | sed 's/controller ".*"/controller "puppyController"/' >> $TRG/worlds/puppy_world.wbt
cat $BASE/supervisor/$SUPERVISOR | sed 's/controller ".*"/controller "supervisorController"/' >> $TRG/worlds/puppy_world.wbt

# copy controller
mkdir -p $TRG/controllers/puppyController
mkdir -p $TRG/controllers/supervisorController
PPTH=( $PCTRL ) # make array
SPTH=( $SCTRL )
PPTH[0]=`readlink -f "${PPTH[0]}"` # replace controller file by absolute path
SPTH[0]=`readlink -f "${SPTH[0]}"`
PPTH="${PPTH[@]}" # rejoin array elements to single string
SPTH="${SPTH[@]}"
cat $BASE/controllers/generic/generic.py | sed "s|^CMD=''|CMD='$PPTH'|" > $TRG/controllers/puppyController/puppyController.py
cat $BASE/controllers/generic/generic.py | sed "s|^CMD=''|CMD='$SPTH'|" > $TRG/controllers/supervisorController/supervisorController.py
#rm $TRG/controllers/puppyController/puppyController.py 2>/dev/null
#rm $TRG/controllers/supervisorController/supervisorController.py 2>/dev/null
#ln -s $PPTH $TRG/controllers/puppyController/puppyController.py
#ln -s $SPTH $TRG/controllers/supervisorController/supervisorController.py


# start webots
cd $TRG
$WEBOTS --mode=$MODE worlds/puppy_world.wbt

Downloads

The above scripts are deployed together with the Puppy model, originally developed and contributed to this project by Matej Hoffmann and Stefan Hutter. All files can be retrieved from the the Download page (see Available downloads).

Table Of Contents

Previous topic

Puppy Python Controller (PuPy)

Next topic

Supervisor

This Page