SLAM system YAML configuration file format

Refer to demos for real examples to launch and run SLAM systems.

File structure

A SLAM system is defined via a YAML configuration file, comprising a top-level modules entry, with one or more children elements with the following required fields:

# my-slam-system.yml
modules:
  - type: CLASS_NAME1       # mandatory
    name: INSTANCE_NAME1    # mandatory
    execution_rate: 20      # Hz (optional)
    verbosity_level: INFO   # DEBUG|INFO|WARN|ERROR
    params:                 # Other module-specific parameters
      var1: value1
      var2: value2
      # ...
  - type: CLASS_NAME2
    name: INSTANCE_NAME2
  # ...

Notes:

  • CLASS_NAME1: The C++ class name of one of the modules registered in the MOLA system.

  • INSTANCE_NAME1: Arbitrary name of this instance of the module. All names must be unique in a SLAM system.

Launching from multiple files

Application: mola-cli accepts more than one YAML file on the command line:

mola-cli sensors.yaml pipeline.yaml visualization.yaml

Each file keeps the exact same self-contained structure described above (a top-level modules entry). mola-cli loads and pre-processes each file independently – so $include{}, $() and ${} paths inside each file are still resolved relative to that file’s own directory – and then merges all of their modules lists into a single running system, in the order the files were given on the command line.

This is useful to split a SLAM system definition into reusable building blocks (e.g. one file per sensor, plus a shared pipeline and visualization file). As with a single file, all module names must be unique across all the given files.

YAML pre-processing

Before being parsed as standard YAML, every configuration file loaded by MOLA is run through a lightweight pre-processor that expands special $-prefixed expressions. The three expansion passes are always applied in the following fixed order:

  1. Include other files: $include{path} - $include{path}

  2. Run an external command: $(command) - $(command)

  3. Variables and environment: ${VAR} / ${VAR|default} - ${VAR} / ${VAR|default}

This ordering means that:

  • $include{} can pull in files whose paths are themselves built with $() or ${} tokens.

  • $() command output can reference environment variables already present in the environment at parse time.

  • ${} variable expansion sees the fully assembled text - including the content of all included files - and can therefore reference variables that are defined in those files.

Comments are never expanded. Any $include{}, $(), or ${} token that appears after a # comment marker on the same line is left completely unchanged. This applies to both inline comments and full-line comments:

v: 1          # $(echo this-is-never-run) ${ALSO_NEVER_EXPANDED}
# ${THIS_IS_ALSO_SAFE}

Include other files: $include{path}

A YAML scalar whose value matches the pattern $include{/path/to/file.yaml} is replaced by the full contents of the referenced file.

  • Paths may be absolute or relative. A relative path is resolved against the directory of the file that contains the $include{} directive. Includes within the included file are in turn resolved relative to that file’s own directory, so any depth of nesting works correctly.

  • The path expression inside $include{} is itself pre-processed by the $() and ${} passes before the file is opened, so the path may be constructed dynamically.

  • Circular includes are detected. If a file attempts to include itself (directly or transitively), a clear error is raised rather than looping indefinitely.

modules:
  - type: CLASS_NAME1
    params:
      # Include a file whose path is built from an environment variable:
      $include{${MY_CONFIG_DIR}/sensor_params.yaml}

    config:
      # Include a file relative to this file's own directory:
      $include{params/lidar_odometry.yaml}

      # Include a file whose path comes from a shell command
      # (e.g. a ROS 2 package share directory):
      $include{$(ros2 pkg prefix mola_lidar_odometry)/share/mola_lidar_odometry/config/lo_default.yaml}

Note

The $include{} directive replaces the entire scalar node it appears in. The typical usage is therefore as the sole value of a map key (params:, config:, etc.), where the included file provides the child map:

params:
  $include{my_params.yaml}   # ← my_params.yaml contributes all keys here

Import a base file and override entries: $import

While $include{} replaces a whole node with the contents of a file, the $import map key lets you load a file as a base and then override only a few of its entries inline. A map containing an $import key is replaced by the deep-merge of the imported file(s) with the map’s remaining keys overlaid on top:

  • The $import value is either a single file path (scalar) or a sequence of paths. When several files are given, they are merged in order, so a later file overrides an earlier one.

  • The sibling keys of $import are then overlaid and therefore override the imported base. Nested maps are merged deeply, so you can override a single deep key without restating its whole subtree; scalars and sequences are replaced wholesale.

  • Paths follow the same rules as $include{} (absolute or relative to the current file’s directory; the path expression itself is pre-processed by the $() / ${} passes), and the same circular-reference detection applies.

This is the recommended way to share a common parameter block across several near-identical launch files while tweaking just a few values, instead of duplicating the whole block:

modules:
  - type: CLASS_NAME1
    name: instance1
    params:
      $import: shared-params.yaml   # base (a path, or a sequence of paths)
      max_rate_hz: 10.0             # override a single entry of the base
      nested:
        gain: 2.0                   # deep-override one nested key (siblings kept)

Equivalent multi-file base, where overrides.yaml wins over base.yaml:

params:
  $import:
    - base.yaml
    - overrides.yaml
  extra_key: 123                    # still overrides both imported files

Note

$import is resolved in the same (first) pass as $include{}. Use $include{} when a node is the file; use $import when you want the file as a base plus local overrides.

Run an external command: $(command)

The pattern $(command arg1 arg2 ...) is replaced by the standard output of the command, with leading and trailing whitespace stripped. Multi-line output is preserved; only the boundary whitespace is removed.

A non-zero exit code causes a fatal error.

This is most commonly used together with $include{} to locate files inside a ROS 2 package:

params:
  $include{$(ros2 pkg prefix my_package)/share/my_package/config/default.yaml}

It can also set scalar values directly:

hostname: $(hostname)
git_sha:  $(git -C ${MY_SRC_DIR} rev-parse --short HEAD)

Warning

Shell commands are executed at parse time, with the same privileges as the process loading the YAML file. Only use $() with commands whose output you trust.

Variables and environment: ${VAR} / ${VAR|default}

The pattern ${NAME} is replaced by a value looked up in the following order (first match wins):

  1. Environment variable - the current value of getenv("NAME").

  2. Built-in token CURRENT_YAML_FILE_PATH - expands to the directory of the file currently being parsed (or the value of YAMLParseOptions::includesBasePath when called programmatically).

  3. Caller-supplied variables - entries in the YAMLParseOptions::variables map, which allows C++ code to inject arbitrary name/value pairs at load time.

  4. Inline default - if the token has the form ${NAME|default_value}, the literal text after | is used as a fallback. The default may be empty (${NAME|} resolves to an empty string).

  5. Error - if none of the above matched, a fatal error is raised.

# Expand an environment variable (fatal if MY_ROBOT is unset):
robot_name: ${MY_ROBOT}

# Use a default value when the variable is not set:
log_dir: ${LOG_DIR|/tmp/mola_logs}

# Empty-string default - never raises an error:
optional_suffix: ${SUFFIX|}

# Refer to the directory of this file (useful for sibling-relative paths):
calibration_file: ${CURRENT_YAML_FILE_PATH}/calib/lidar.yaml

# The same variable may appear multiple times in one scalar:
topic: /${ROBOT_NS}/sensors/${SENSOR_NAME}/points

Note

Substituted values are not re-expanded. If an environment variable itself contains a ${...} or $(...) token, that token is emitted verbatim and not processed a second time. This prevents accidental double-expansion and avoids infinite loops.

Tip

You can combine all three mechanisms. For example, the following snippet locates a calibration file in a ROS 2 package, using an environment variable to choose the robot variant, with a safe default:

calibration:
  $include{$(ros2 pkg prefix my_robot_config)/share/my_robot_config/calib/${ROBOT_VARIANT|default}/lidar.yaml}

Mathematical formulas: $f{expr}

The pattern $f{expr} evaluates a mathematical expression using the ExprTk library (via mrpt-expr). Refer to the ExprTk documentation for the full list of built-in functions and operators. User-defined variables may be available depending on the context in which the YAML file is parsed.

half_fov_rad: $f{deg2rad(45.0)}
diagonal:     $f{sqrt(3.0^2 + 4.0^2)}

Processing order summary

The table below summarises which pass handles each token, and what happens when a token falls inside a comment:

Token

Replaced with

Pass

In a # comment

$include{path}

Contents of path

1st

Left verbatim

$import (map key)

Imported file(s), with sibling keys overriding

1st

n/a (a map key, not a scalar token)

$(command)

stdout of command

2nd

Left verbatim (command not run)

${VAR}

Value of VAR (error if unset)

3rd

Left verbatim

${VAR|default}

Value of VAR, or default

3rd

Left verbatim

$f{expr}

Result of mathematical expression

Context-dependent

Left verbatim

Debugging pre-processing

Set the environment variable VERBOSE=1 before launching any MOLA executable to print each external file ($include{} or $import) as it is resolved:

VERBOSE=1 ros2 launch my_slam_system slam.launch.py

Each resolved file prints a line of the form:

[mola::parse_yaml] loading external YAML: "/absolute/path/to/file.yaml"