Modules
Modules use code generation in order to allow people to use them by only editing the vehicle’s XML configuration
as opposed to changing/adding to the code of the autopilot. This makes the paparazzi build process highly configurable.
Typically, function calls are generated for initialization and in the main loop for timers
(periodic
in Paparazzi slang) and events. The event and periodic functions of the Modules get called at the end of
event_task_ap
and periodic_task_ap respectively
.
Modules are used to configure all parts of Paparazzi other than the firmware
sections.
The build configuration for each module is described using XML description language and the located in the
conf/modules
subfolder. These modules implement not only the user implemented mission specific code but also the
low level drivers and subsystems.
sw/airborne/modules
contains the mission specific software such as image processing, video recording or relay or
guidance commands.
Using existing modules
An autogenerated list of all the available modules in Paparazzi can be found on http://docs.paparazziuav.org/.
To add a module to an aircraft, add a section modules in his airframe XML file :
Arbitrary airframe file: conf/airframes/myplane.xml
<firmware name="fixedwing">
<module name="my_module">
<define name="BLA" value="1"/>
<define name="BLUB"/>
<configure name="FOO" value="BAR"/>
</module>
</firmware>
All modules must be included within firmware sections. The firmware implements some vehicle specific code using
predefined Makefiles. The most common firmware
types are rotorcraft
and fixedwing
, but more exist and are
all located in the conf/firmware
folder.
A module will consist of a module
node that specifies the name of the module that should be included (in this case
name="my_module"
).
The children define
and configure
are optional and module specific. They can be used to specify the value of
certain variables used in the module. A define
will generate compilation defines for all the targets of a module
and can be used with or without an associated value. For more information on define
and configure
have a look at
the section Airframe Defines and GCS Settings.
Make Your Own
It is very possible to make your own module and to share it with the world. To make it even easier there is a helper tool
called create_mod_qt.py
that can be found in sw/tools/create_module/
. To run it you will need Python 3 and a
few Python packages that can be installed by running in your terminal:
cd paparazzi/sw/tools/create_module
python3 -m pip install -r requirements.txt
python3 create_mod_qt.py
This program can also be launched directly from the Paparazzi Center, by selecting Tools -> Module Creator
from the
Menu bar.
Try it, you’ll be surprised how easy it is to start your own module with the help of this tool.
Another way to go about creating your module is by copying another module’s files and editing the relevant parts. In order to do this, it is necessary to understand a bit more about how modules work in Paparazzi, starting with where all the files of a module are located.
Module Files Location
Modules consist of the following files: a module XML file, source and header files.
├── conf
│ ├── airframes
│ ├── autopilot
│ ├── flight_plans
│ ├── modules <---- Contains module XML with description, defines, compilation instructions
│ ├── radios
│ ...
├── doc
├── sw
│ ├── airborne
│ │ └── modules <---- Contains module source and header files
│ ├── ext
│ ...
...
Module XML
The module description files are located in conf/modules/
. They contain information such as a description of the module,
module settings that can be controlled from the GCS, which source files are needed to compile the module, and additional
module specific compiler flags.

Here is an example of a module XML file. Not all of the XML nodes that are shown are actually required, as will be explained in this section.
<!DOCTYPE module SYSTEM "module.dtd">
<module name="demo_module">
<doc>
<description>
Demo module
</description>
<configure name="SOMETHING" value="S1|S2|S3" description="The thing to use"/>
<define name="DEMO_MODULE_LED" value="LED_X" description="LED Selection"/>
</doc>
<settings>
<dl_settings name="bla">
<dl_setting min="0" max="5" step="1" var="bla_bla" shortname="bb"/>
</dl_settings>
<settings>
<dep>
<depends>module1,module2|module3,@functionality1</depends>
<provides>functionality2</provides>
<conflicts>module4,@functionality3</conflicts>
</dep>
<header>
<file name="demo_module.h"/>
</header>
<init fun="init_demo()"/>
<periodic fun="periodic_1Hz_demo()" freq="1." start="start_demo()" stop="stop_demo()" autorun="TRUE"/>
<periodic fun="periodic_10Hz_demo()" period="0.1" start="start_demo()" stop="stop_demo()" autorun="FALSE"/>
<makefile>
<raw>
#Example of RAW makefile part
</raw>
<define name="DEMO_MODULE_LED" value="2"/>
<file name="demo_module.c"/>
</makefile>
<makefile target="demo">
<define name="SOME_FLAG"/>
<configure name="SOME_DEFINE" value="bla"/>
</makefile>
</module>
The XML file starts with a module
element that sets the name of the module (in this case demo_module
).
Optionally, this element can contain a dir
attribute as well, to specify the location of the source files relative to
sw/airborne/modules/
.
In this case the directory is not provided since the source files are located in a directory inside sw/airborne/modules/
that has the same name as the module name (sw/airborne/modules/demo_module/
).
After a documentation and dependency section, the XML contains a header element, where the header files of the module are listed. Typically, you will only see one header file here that provides an easy-to-use access point for other modules.
The header element is often followed by an init
and periodic
element.
These specify what functions in your module code should be called by the autopilot, and in case of the periodic function
it also specifies its frequency in Hz. The other two function types that can be specified consist of event
and
datalink
functions.
At the end of the XML file is the makefile element. This section describes how your source files should be compiled.
Simple modules such as the demo_module only list one or more source files. More complicated modules such as
cv_opencvdemo
can specify additional compiler flags (to link OpenCV, for example) and can have different
makefile sections depending on whether the autopilot is compiled for use on the drone (target="ap"
) or in
simulation (target="nps"
).
The source and header files of your module can be found in sw/airborne/modules/<your_module_dir>/
.
We take a closer look at the content of these files in the Header and
Source Sections.
Here is an overview of all possible Module XML nodes:
Node |
Children |
Description |
---|---|---|
module
(required)
|
name (required) |
This parameter is the name of the module (mandatory) |
dir |
The name of the directory in |
|
doc
(optional)
|
description (required) |
A description of the module. The content of the first line until the dot is treated as the brief description used as the name in the generated docs |
define |
Describe the possible define flags for this module with default values and a short description (usually called from the airframe firmware section |
|
configure |
Describe the possible configuration options for this module with default values and a short description (usually called from the airframe firmware section |
|
section |
Describe the parameters that can be added as a section in the airframe configuration file |
|
settings
(0 or more)
|
target |
A list of targets allowed or forbidden for which embedded settings should be used |
dl_settings |
Creates a tab with arbitrary name that can be specified with
|
|
dl_setting (child of dl_settings) |
Setting description, see Settings section for details |
|
dep
(0 or 1)
|
depends |
Comma separated list of required modules Allows to specify OR dependencies with pipe
(|) similar to Debian depends, ex: The elements can be a module name (as set in the module XML |
provides |
Advertises the functionality that the module provides (e.g. actuators, imu) |
|
conflicts |
Comma separated list of conflicting modules The elements can be a module name (as set in the module XML |
|
autoload
(0 or 1)
|
name |
The name of the module which should also be automatically loaded |
header
(0 or 1)
|
file |
The name of the header to automatically include in modules.h |
init
(0 or more)
|
fun |
Initialization function name, called once at startup |
periodic
(0 or more)
|
fun (required) |
Periodic function name |
period |
Period of the function in seconds, cannot be higher than the main frequency (if not specified, use freq parameter) |
|
freq |
Frequency of the function in Hz, cannot be higher than main frequency (used if period is not defined; if nor period nor freq are defined, the maximum frequency is used by default) |
|
delay |
Integer that can be used to impose a sequence in the periodic functions (use values between 0. and 1.) |
|
start |
Function to be executed before the periodic function starts |
|
stop |
Function to be executed after the periodic function stops (never called
if |
|
autorun |
TRUE to make the periodic function starts automatically after init, FALSE to make it way for a user command to start, LOCK to make it always true (default is LOCK) |
|
event
(0 or more)
|
fun |
Event function name called in each cycle of the main AP loop |
datalink
(0 or more)
|
message |
Name of the datalink (uplink) message to be parsed |
fun |
Name of the function called when a message arrived |
|
makefile
(0 or more)
|
target |
A list of build targets separated with pipes
(ex: |
define |
Each define node specifies a CFLAGS for the current targets
|
|
file |
|
|
file_arch |
|
|
raw |
Allows to define a raw makefile section |
Starting and Stopping a module
Together with the periodic function, the module XML can specify a START
and STOP
function. These are called when
the module is started or stopped, respectively. The autorun
attribute in the module XML’s periodic
element
controls whether your module is started automatically or manually; you can manually start and stop modules from the GCS
by going to Settings -> System -> Modules
, selecting START
or STOP
and clicking the green checkmark.
You can find an example of start and stop functions functions in sw/airborne/modules/loggers/file_logger.c
,
where they are used to open and close the log file.
If modules are loaded with periodical functions that are not locked, a new tab will automatically appear in the setting page of the GCS that allows you to start and stop them.
An other possibility is that any file that includes the header “modules.h” can start or stop the periodic tasks.
Module Header File
The module header is located in sw/airborne/modules/<module-dir>/<module-header.h>
, and functions like a normal .h
file. The main difference is that any function or variable that is referenced by an XML file needs to be defined as
extern
so that the compiler can find the definition.
By convention any variable and function that is defined in a module header, especially if used outside of the module by another module or XML, should be prefixed with the module name or some other unique identifier to help avoid name collision.
Module Source File
The autopilot will regularly call functions that are part of your module, such as a module periodic function. Which functions are called is defined by the module XML file described earlier.
The section Module XML lists the types of functions you can register in the module XML: init
, periodic
,
event
and datalink
, of which init and periodic are the most common.
The init
function is called once at startup. You can use this function to initialize important variables of your
module, or memory intensive structures such as large arrays, or for instance to subscribe to new video frames.
Once the autopilot is fully initialized, it will enter an infinite loop in which it will continuously read new sensor
data, feed this to the guidance and stabilization controllers, and send new commands to the actuators.
From this loop, the autopilot can also call your module’s periodic
function at a frequency specified in the
module XML.
Within this function, you can for instance get the drone’s state and use this to calculate new setpoints for the
guidance controller.
Because the periodic function is called from within the autopilot’s control loop, you should take care that the function does not take too much time to run. The autopilot runs by default at 512~Hz, which means that it has slightly less than 2~ms to run your module code, the code of the other modules and the control loops and estimators. If your periodic function takes too long, the autopilot will run at a lower frequency than intended, which can lead to instability. In practice you have to make things pretty bad before this becomes a problem, but you should be careful when using large or nested loops in your periodic function, and video processing is best performed in the video callback function, as this callback runs in a separate thread.
Warning
If your periodic function takes too long, the autopilot will run at a lower frequency than intended, which can lead to instability
Airframe Defines and GCS Settings
A module will most likely contain tunable parameters, such as gain or threshold values. While these numbers can be written directly in the source code, this will make it difficult to tune them later, as every time that they are changed you will need to rebuild and reupload to your drone. Paparazzi provides two systems to simplify parameter tuning: defines and settings.
Defines allow you to set constant values from the airframe file. See, for example, the following abstract of the
bebop_course_orangeavoid.xml
airframe:
<airframe name="bebop_avoider">
<firmware name="rotorcraft">
<target name="ap" board="bebop">
<define name="COLOR_OBJECT_DETECTOR_LUM_MIN1" value="40"/>
<!-- ... -->
</target>
<!-- ... -->
<define name="ARRIVED_AT_WAYPOINT" value="0.5"/>
<!-- ... -->
<module name="cv_detect_color_object">
<define name="COLOR_OBJECT_DETECTOR_CAMERA1" value="front_camera"/>
<!-- ... -->
</module>
</firmware>
<!-- ... -->
<section name="GUIDANCE_H" prefix="GUIDANCE_H_">
<define name="CLIMB_VSPEED" value="1.0"/>
</section>
<!-- ... -->
</airframe>
As you can see, defines can be set at multiple places in the airframe file. The behavior is mostly the same in these cases, with the following exceptions:
Defines placed in the
<target>
elements are only set when the autopilot is built for that target, i.e."ap"
for the real drone and"nps"
for the simulator. This allows you to, for instance, use different color filter settings on the real and simulated drone.Placing a define inside a
<module>
element has no special effect! The define is also visible in other modules, so be sure to use a unique name. Typically, defines are prefixed with the name of the module (e.g.COLOR_OBJECT_DETECTOR_
to make them unique. The only reason these defines are placed inside the module element is to improve readability.<section>
elements allow you to specify aprefix
, this prefix is placed in front of all define names inside this section. In the example, theCLIMB_VSPEED
define is available in the code asGUIDANCE_H_CLIMB_VSPEED
.
During compilation, these defines are turned into preprocessor macros and can be referred to directly from your code.
Airframe defines allow you to set constant parameters at compile-time, but in some cases it would be easier if you
could change these values during the flight. This is possible with the
settings mechanism. Settings are defined in the module XML file.
Take for example conf/modules/cv_detect_color_orange.xml
:
<module name="cv_detect_color_object">
<!-- ... -->
<settings>
<dl_settings name="ColorObjectDetector">
<dl_setting var="cod_lum_min1" min="0" step="1" max="255" shortname="y_min1"/>
<!-- ... -->
</dl_settings>
</settings>
</module>
Settings listed in the module XML can be tuned from the
Ground Control Station by going to the Settings tab and then selecting the tab belonging
to your module, as defined in the dl_settings
element (here ColorObjectDetector
). To read the current value of
a parameter from the drone, click its value (the number) in the GCS. Te set a value on the drone, adjust the slider,
then click the green checkmark to upload this new value to the drone . Click the value number again to make sure the
setting was updated if a question mark appears to the left of the slider. The updated value should appear to the left
of the slider.
Use the dl_setting
element in your module XML to add a setting to your module. The var
attribute
specifies the variable this setting should be written to; this variable should be globally accessible (defined as
extern
in the h file).
The min
, step
and max
attributes let you specify a range of possible values for this setting.
Using shortname
you can control the name under which this setting is listed in the GCS.
The module
attribute can be added to specify the file where the variable is coming from.
A corresponding #include “m.h” will be auto-generated in the corresponding C code.
In case of more complicated logic that needs to be triggered any time that a GCS variable is changed (like resetting
certain variables, or changing the value of more variables at once) a handler
attribute can be added to specify
a function (or a macro) to be called whenever the setting is changed. This macro is associated with a module and
must be named <module-name>_<handler-name>()
.
As an example, take a look at an excerpt from conf/modules/digital_cam.xml
:
<dl_settings name="dc">
<dl_setting max="255" min="0" step="1" module="digital_cam/dc" var="0" handler="send_command" shortname="Shutter">
</dl_settings>
The module
attribute is specified as module="digital_cam/dc"
. While in the XML the handler
function is specified as send_command
, in the source code the module name must be added in front of the function
name, as can be seen in sw/airborne/modules/digital_cam/dc.h
.
extern void dc_send_command(uint8_t cmd);
It is possible to combine the define and settings mechanisms, where the define provides a default value that can be adjusted later using settings. This often uses the following pattern:
#ifndef MY_DEFINE
#define MY_DEFINE 0
#endif
int my_setting = MY_DEFINE;
In this example, MY_DEFINE
provides the initial value of my_setting
. MY_DEFINE
can be set from
the airframe file, but if it is not defined there this code will give it a default value of 0. The actual parameter
is stored in my_setting
, for which a <dl_setting>
element is included in the module’s XML file.
Third Party Modules
It is possible to include third party modules in an airframe, or modules that are not located within the Paparazzi
folder itself. The extra directories can be added with the environment variable PAPARAZZI_MODULES_PATH
, where
items are :
separated and modules are in subfolders of a modules folder.
Ex. PAPARAZZI_MODULES_PATH=/home/me/pprz_modules
. This directory should look like this:
├── pprz_modules
│ ├── modules
│ │ ├── module1
│ │ │ ├── module1.xml
│ │ │ ├── module1.h
│ │ │ └── module1.c
│ │ ├── module2
│ │ │ ├── module2.xml
│ │ │ ├── module2.h
│ │ │ └── module2.c
| │ ...
| ...
...