6.6 Controller plugin
The controller functionnality can be extended with user-implemented plugins. The purpose of the controller plugins is to facilitate the programming of robot-specific robot windows and remote-control wrappers.
Programming controller plugin rather than programming directly in the controller is more convenient because it increases considerably the modularity and the scalability of the code. For example a robot window can be used for several robots.
6.6.1 Fundamentals
Whatever its language, a controller executable is linked with the Webots controller library (libController) at startup. A controller plugin is a shared library loaded dynamically (at runtime) by libController after a specific event depending on its type.
The figure 6.3 shows an overview of the controller plugin system. In this figure, the dashed arrows shows how the shared libraries are loaded, and the large dash lines represents an Inter-Process Communication (IPC). The IPC between libController and Webots is a pipe (On Windows this is a named pipe, and otherwise a local domain socket). The IPC between libRemoteControl and the real robot is defined by the user (TCP/IP, Serial, etc.).
The system has been designed as follow. Every entities (the controller, the remote control library and the robot window library) should only call the libController interface (Webots API) functions. The controller should not be aware of its robot window and its real robot for modularity reasons. The only exception is about the robot window library which can be aware of the remote control library to initialise and monitor it. This can be done trough the libController API through the wb_robot_get_mode(), wb_robot_set_mode() and the wb_remote_control_custom_function() functions. Of course these rules can be easily broken because every entities runs into the same process. However we recommend to respect them to get a good design.
The controller plugins have been designed to be written in C/C++, because the result should be a dynamic library. However it's certainly possible to write them in other languages using a C/C++ wrapper inbetween.
After its loading, some controller plugin functions (entry points) are called by libController. A set of entry points have to be defined to let the controller plugin work smoothly. Some of these entry points are required and some are optional.
The Robot node defines the location of the controller plugin through its libRobotWindow and its libRemoteControl fields (cf. reference manual)
The controller plugins run in the main thread of the process (also known as Gui thread): the same a the controller executable. This implies that if an entry point of a plugin is blocking, the controller will also be blocked. And if the plugin crashes, the controller is also crashed.
The extension of the shared library file is .dll on windows, .so on linux, and .dylib on mac. Controller plugins are designed to be located in the lib directory of the project directory. Moreover the shared library should be created in a subdirectory starting with the lib prefix, and with the same basename as the shared library. For example: my_project/lib/libmyrobotwindow/libmyrobotwindow.so
Each distributed shared library is built thanks to a Makefile which includes this file: $WEBOTS_HOME/resources/projects/default/lib/Makefile.include

Figure 6.3: Controller plugins overview
6.6.2 Robot window plugin
The robot window plugin allows to create simply and efficiently custom robot windows. The robot windows can be open by double-clicking on the virtual robot, or by selecting the Robot | Show Robot Window menu item.
The robotWindow field of the Robot node allows to select which robot window (cf. documentation in the reference manual).
The entry points of the robot window controller plugin are:
-
bool wbw_init(int argc, char *argv[], int pipeHandle)
This is the first function called by libController. Its aim is to initialize the graphical user interface without showing it. The arguments argc and argv are the standard arguments used to launch the controller (The first argument is the controller name, and then it's the content of the Robot controllerArgs field). The pipeHandle argument corresponds to the id of the pipe between the controller and Webots. This will be useful in the wbw_(pre_)update_gui() functions.
-
void wbw_cleanup()
This is the last function called by libController. Its aim is to cleanup the library (destroy the GUI, release the memory, store the current library state, etc.)
-
void wbw_pre_update_gui()
This function is called before wbw_update_gui() to inform its imminent call. Its purpose is to inform that from this moment, the pipe answering from Webots to the controller can receive data. If data is comming from the Webots pipe wbw_update_gui() should return as soon as possible.
-
void wbw_update_gui()
The aim of this function is to process the GUI events until something is available on the Webots pipe.
-
void wbw_read_sensors()
This function is called when it's time to read the sensors values from the Webots API. For example in this function the wb_distance_sensor_get_value() function can be call.
-
void wbw_write_actuators()
This function is called when it's time to write the actuator commands from the Webots API. For example in this function the wb_servo_set_position() function can be call.
-
void wbw_show()
This function is called when the GUI should be show. This can occur either when the user double-click on the virtual robot, either when he selects the Robot | Show Robot Window menu item, or either at controller startup if the showRobotWindow field of the Robot node is enabled.
The internal behavior of the wb_robot_step() call is the key point to understand how the different entry points of the robot window plugin are called (pseudo-code):
wb_robot_step() { |
As the Qt libraries are included in Webots (used by the Webots GUI), and all our samples are based on it, we recommend to choose also this framework to create your GUI. The Makefile.include mentionned above allows you to linked efficiently with the Qt framework embedded in Webots.
If the robot window cannot be loaded (bad path, bad initialization, etc.), a generic robot window is open instead. This generic robot window display several sensors and actuators. The source code of this robot window is a good demonstrator of the robot window plugin abilities. All the source code is located there: $WEBOTS_HOME/resources/projects/default/lib/libgenericwindow
Other samples can be found:
$WEBOTS_HOME/resources/projects/default/lib/libbotstudio
$WEBOTS_HOME/resources/projects/robots/e-puck/lib/libepuckwindow
6.6.3 Remote-control plugin
The remote-control plugin allows to create simply and efficiently an interface using the Webots API to communicate with a real robot. The main purpose of a remote-control library is to wrap all the Webots API functions used by the robot with a protocol communicating to the real robot. Generally, a program (client) runs on the real robot, and decodes the communication protocol to dialog with the real robot devices.
The remote-control library is initialized when an entity calls the wb_robot_set_mode() libController function. This entity is typically libRobotWindow, because it's quite convenient to use the GUI to initialize the communication (i.e. entering the IP address of the robot, etc.)
There are two entry points to the remote-control library:
-
bool wbr_init(WbrInterface *ri)
This function is called by libController to initialize the remote control library. It is called after the first wb_robot_set_mode() call. The aim of this function is to map the functions given into the WbrInterface structure with functions inside the remote-control library.
-
void wbr_cleanup()
This function is called by libController to cleanup the library.
The WbrInterface structure has several functions (mandatory) which have to be mapped to let the remote-control library runs smoothly. Here they are:
-
bool wbr_start(void *arg)
This function is called when the connection with the real robot should start. The return value of this function should inform if the connection has been a success or not. The argument matches with the argument given to wb_robot_set_mode() when initializing the remote-control. As the robot window library is often responsible in calling wb_robot_set_mode(), the structure passed between them should match.
-
void wbr_stop()
This function is called when the connection with the real robot should stop. Typically a command stopping the real robot actuators should be sent just before stopping the connection.
-
bool wbr_has_failed()
This function is called very often by libController to check the validity of the connection. The value returned by this function should always match with the connection validity.
-
void wbr_stop_actuators()
This function is called to stop the actuators of the real robot. This is called when the user pressed the stop button of the simulator.
-
int wbr_robot_step(int period)
This function is called when the controller enters in the step loop. The aim of this function is to send the actuator commands and then to read the vaues of the enabled sensors. The timing problem should be solved there. The robot should wait at least period milliseconds, and returns the delta time if this period is exceeded.
As said above, all the Webots API functionnalities that should work with the real robot have to be wrapped into the remote-control library. To achieve this:
-
The internal state of the libController has to be setup to match with the current state of the robot.
Typically, when the value of a sensor is known the corresponding wbr_sensor_set_value() has to be called.
-
The commands send to the libController have to be wrapped.
Typically, when the command of an actuator is setup the corresponding wbr_actuator_set_value() is called, and has to be send to the real robot.
This file contains the complete definition of the remote control API and of the WbrInterface structure: $WEBOTS_HOME/include/controller/c/webots/remote_control.h
For example, if you want to be able to use the distance sensor of the real robot, you have to wrap the wbr_set_refresh_rate() function (to set the internal state of the remote control library to read this distance sensor only when required), and to calls wbr_distance_sensor_set_value() into the remote-control library when the distance sensor is refresh (typically into the wbr_robot_step() function).
A complete sample (communicating with the e-puck robot using bluetooth) can be found in this directory:
$WEBOTS_HOME/resources/projects/robots/e-puck/lib/libbluetooth