diff --git a/doc/sphinx/source/_static/custom.css b/doc/sphinx/source/_static/custom.css new file mode 100644 index 0000000000..8aca23e02f --- /dev/null +++ b/doc/sphinx/source/_static/custom.css @@ -0,0 +1,4 @@ +/* Removes table horizontal scrollbar and fits table content to page */ +.wy-table-responsive table td { +white-space: normal; +} diff --git a/doc/sphinx/source/conf.py b/doc/sphinx/source/conf.py index 4484e03daa..5ee07a2fc1 100644 --- a/doc/sphinx/source/conf.py +++ b/doc/sphinx/source/conf.py @@ -167,6 +167,10 @@ html_theme = 'sphinx_rtd_theme' # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] +html_css_files = [ + 'custom.css', +] + # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. diff --git a/doc/sphinx/source/developer_guide/module_architecture_overview.png b/doc/sphinx/source/developer_guide/module_architecture_overview.png new file mode 100644 index 0000000000..3235b10fff Binary files /dev/null and b/doc/sphinx/source/developer_guide/module_architecture_overview.png differ diff --git a/doc/sphinx/source/developer_guide/modules.rst b/doc/sphinx/source/developer_guide/modules.rst index bd6c5693f3..5073035352 100644 --- a/doc/sphinx/source/developer_guide/modules.rst +++ b/doc/sphinx/source/developer_guide/modules.rst @@ -4,6 +4,538 @@ Modules ======== -TBD +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 +-------------------------- +To add a module to an aircraft, add a section modules in his airframe XML file : + +Arbitrary airframe file: conf/airframes/myplane.xml + +.. code-block:: xml + + + + + + + + + + + +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 :ref:`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: + +.. code-block:: bash + + 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. + +.. code-block:: text + + ├── 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 + │   ... + ... + + +.. _label-module-xml: + +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. + +.. image:: module_architecture_overview.png + +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. + +.. code-block:: xml + + + + + + + Demo module + + + + + + + + + + + + + module1,module2|module3,@functionality1 + functionality2 + module4,@functionality3 + + +
+ +
+ + + + + + + + + #Example of RAW makefile part + + + + + + + + + + +
+ + +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//``. +We take a closer look at the content of these files in the :ref:`Header ` and +:ref:`Source ` Sections. + +Here is an overview of all possible Module XML nodes: + + ++---------------+---------------+-------------------------------------------------------------------------+ +| Node | Children | Description | ++===============+===============+=========================================================================+ +| | name | This parameter is the name of the module (mandatory) | +| | (required) | | +| | module +---------------+-------------------------------------------------------------------------+ +| | (required) | | The name of the directory in ``sw/airborne/modules`` where the source | +| | dir | code is located. If not specified, the name of the module is used as | +| | | default directory name | ++---------------+---------------+-------------------------------------------------------------------------+ +| | description | A description of the module. The content of the first line until | +| | (required) | 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 | +| | doc | | firmware section | +| | (optional) +---------------+-------------------------------------------------------------------------+ +| | 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 | ++---------------+---------------+-------------------------------------------------------------------------+ +| | target | A list of targets allowed or forbidden for which embedded settings | +| | | should be used | +| | settings +---------------+-------------------------------------------------------------------------+ +| | (0 or more) | dl_settings | Creates a tab with arbitrary name that can be specified with | +| | | ``name="your-tab-name"`` | +| +---------------+-------------------------------------------------------------------------+ +| | dl_setting | Setting description, see :ref:`Settings ` | +| | (child of | section for details | +| | dl_settings) | | +| | | | +| | | | ++---------------+---------------+-------------------------------------------------------------------------+ +| | | Comma separated list of required modules | +| | dep | | | +| | (0 or 1) | depends | | +| | | Allows to specify OR dependencies with pipe | +| | | (\|) similar to Debian depends, ex: ``module1,module2|module3`` | +| | | would make it depend on ``module1 AND (module2 OR module3)`` | +| | | | +| | | The elements can be a module name (as set in the module XML ``name`` | +| | | node) or a functionality (a keyword specified in a ``provides`` node), | +| | | which has to be preceded by @ | +| +---------------+-------------------------------------------------------------------------+ +| | 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 ``name`` | +| | | node) or a functionality (a keyword specified in a ``provides`` node), | +| | | which has to be preceded by @ | ++---------------+---------------+-------------------------------------------------------------------------+ +| | autoload | name | The name of the module which should also be automatically loaded | +| | (0 or 1) | | | ++---------------+---------------+-------------------------------------------------------------------------+ +| | header | file | The name of the header to automatically include in modules.h | +| | (0 or 1) | | | ++---------------+---------------+-------------------------------------------------------------------------+ +| | init | fun | Initialization function name, called once at startup | +| | (0 or more) | | | ++---------------+---------------+-------------------------------------------------------------------------+ +| | periodic | fun | Periodic function name | +| | (0 or more) | (required) | | +| +---------------+-------------------------------------------------------------------------+ +| | 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=LOCK``) | +| +---------------+-------------------------------------------------------------------------+ +| | 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 | fun | Event function name called in each cycle of the main AP loop | +| | (0 or more) | | | ++---------------+---------------+-------------------------------------------------------------------------+ +| | datalink | message | Name of the datalink (uplink) message to be parsed | +| | (0 or more) +---------------+-------------------------------------------------------------------------+ +| | fun | Name of the function called when a message arrived | ++---------------+---------------+-------------------------------------------------------------------------+ +| | makefile | target | A list of build targets separated with pipes | +| | (0 or more) | | (ex: ````) | +| | | (default is ``ap|sim|nps``) | +| +---------------+-------------------------------------------------------------------------+ +| | define | Each define node specifies a CFLAGS for the current targets | +| | | | +| | | - | `name` : name of the define (ex: ``name="USE_MODULE_LED"`` -> | +| | | | ``target.CFLAGS += -DUSE_MODULE_LED``) (required) | +| | | | +| | | - | `value` : the value to associate | +| | | | (ex: ``name="DEMO_MODULE_LED" value="2"`` -> | +| | | | ``target.CFLAGS += -DDEMO_MODULE_LED=2``) | +| | | | +| | | - | `type` : the type of define, possible values are "define" or "D", | +| | | | "include" or "I" (ex: ``name="$(ARCH_SRC)" type="include"`` -> | +| | | | ``target.CFLAGS += -I$(ARCH_SRC)`` default is "define" | +| +---------------+-------------------------------------------------------------------------+ +| | file | - | `name` : the name of the c file (located in | +| | | | ``sw/airborne/modules/``) to add in the Makefile | +| | | | (ex: ``name="demo_module.c"`` -> | +| | | | ``target.srcs += modules//demo_module.c)`` | +| | | | +| | | - | `dir` : select a directory for this file only | +| | | | (overrides thedefault directory) | +| | | | +| | | - | `cond` : allows for the conditional compilation of file depending | +| | | | on the condition specified (ex. ``cond="ifdef FOO"`` -> | +| | | | ``ifdef FOO`` | +| | | | ``...`` | +| | | | ``endif`` | +| | | | As the ``file`` node refers to compilation elements, ``ifdef``, | +| | | | ``ifeq`` etc. must be specified in value of the ``cond`` attribute | +| +---------------+-------------------------------------------------------------------------+ +| | file_arch | - | `name` : the name of the c file (located in | +| | | | ``sw/airborne/arch//modules/``) add in the Makefile | +| | | | (ex: ``name="demo_module_hw.c"`` -> | +| | | | ``target.srcs += arch//modules//demo_module_hw.c``) | +| | | | +| | | - | `dir` : select a directory for this file only | +| | | | (overrides the default directory) | +| | | | +| | | - | `cond` : allows for the conditional compilation of file depending | +| | | | on the condition specified (ex. ``cond="ifdef FOO"`` -> | +| | | | ``ifdef FOO`` | +| | | | ``...`` | +| | | | ``endif`` | +| | | | As the ``file`` node refers to compilation elements, ``ifdef``, | +| | | | ``ifeq`` etc. must be specified in value of the ``cond`` attribute | +| +---------------+-------------------------------------------------------------------------+ +| | 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. + + +.. _label-module-header-file: + +Module Header File +--------------------- + +The module header is located in ``sw/airborne/modules//``, 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. + + +.. _label-module-source-file: + +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 + + +.. _label-defines-and-settings: + +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: + +.. code-block:: xml + + + + + + + + + + + + + + + + +
+ +
+ +
+ +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 ```` 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 ```` 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. + +- ``
`` elements allow you to specify a ``prefix``, this prefix is placed in front of all + define names inside this section. In the example, the ``CLIMB_VSPEED`` define is available in the code as + ``GUIDANCE_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``: + +.. code-block:: xml + + + + + + + + + + + +`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** ``_()``. + +As an example, take a look at an excerpt from ``conf/modules/digital_cam.xml``: + +.. code-block:: xml + + + + + +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``. + +.. code-block:: C + + 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: + +.. code-block:: C + + #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 ```` 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: + +.. code-block:: text + + ├── pprz_modules + │  ├── modules + │  │  ├── module1 + │  │  │  ├── module1.xml + │  │  │  ├── module1.h + │  │  │  └── module1.c + │  │  ├── module2 + │  │  │  ├── module2.xml + │  │  │  ├── module2.h + │  │  │  └── module2.c + |  │  ... + |  ... + ... diff --git a/doc/sphinx/source/index.rst b/doc/sphinx/source/index.rst index 85e595c89c..68f0060c5e 100644 --- a/doc/sphinx/source/index.rst +++ b/doc/sphinx/source/index.rst @@ -28,8 +28,8 @@ Contents: :maxdepth: 2 quickstart/index_quick_start - user_guide/index_user_guide installation/index_installation + user_guide/index_user_guide developer_guide/index_developer tutorials/index_tutorials support/index_support