Skip to main content
Basis provides a structured argument system for configuring units and launch files at runtime. Arguments are declared in YAML, parsed from the command line or launch file, and made available to the unit at construction time.

Unit arguments

Arguments for a unit are declared in its .unit.yaml file under the args key. Each argument has a type, an optional help string, and either a default value or an optional flag.
args:
  string_arg:
    type: string
    help: a string argument
  int_arg:
    type: int
  optional_string_arg:
    type: string
    default: foobar

Argument fields

FieldRequiredDescription
typeYesC++ type for the argument. See supported types below.
helpNoHelp text shown on argument parse errors and --help.
defaultNoDefault value if the argument is not provided. Mutually exclusive with optional.
optionalNoWhen true, wraps the generated field in std::optional<T>. Mutually exclusive with default.

Supported argument types

The following types are valid for the type field:
bool
string
uint8_t   int8_t
uint16_t  int16_t
uint32_t  int32_t
uint64_t  int64_t
float
double
These correspond directly to C++ types. The type system is defined via the X_ALLOWED_ARGUMENT_TYPES macro in cpp/arguments/include/basis/arguments/argument_types.h.

The CommandLineTypes system

CommandLineTypes is a std::variant that abstracts over three ways to supply arguments to a unit or launch file:
using CommandLineTypes = std::variant<
  std::vector<std::pair<std::string, std::string>>,  // key-value pairs
  std::vector<std::string>,                          // argv-style strings
  std::pair<int, char const* const*>                 // raw argc + argv from main()
>;
All three forms are ultimately converted to an argparse-compatible std::vector<std::string> before parsing. This lets the same argument declaration work whether arguments come from a launch file, a direct process invocation, or a test harness.

Parsing unit arguments in C++

Code-generated unit classes use UnitArguments<T>::ParseArgumentsVariant() to parse a CommandLineTypes into a typed struct:
auto result = MyUnitArgs::ParseArgumentsVariant(command_line);
if (!result) {
  // result.error() contains the parse error message
}
auto args = *result;  // typed struct with fields per declared arg
The CreateArgumentParser() static method produces an argparse::ArgumentParser pre-populated with all declared arguments, which can be used for generating --help output.

Launch file arguments

Launch files support a Jinja2-style templating system (via the inja library). Arguments are declared in a separate YAML document at the top of the launch file, separated from the content by ---.
---
args:
  topic_prefix:
    type: string
    help: namespace prefix for all topics
    default: /robot
  enable_debug:
    type: bool
    default: False
---
groups:
  perception:
    units:
      camera_driver:
        args:
          topic: "{{args.topic_prefix}}/camera"
The first document (before ---) declares the argument schema. The second document is the launch content and may use {{ args.name }} expressions anywhere in the YAML text.
The args key must appear only in the first document. If args is detected in the content document, the launch parser will report a fatal error.

Passing arguments to a launch file

Pass arguments after the launch file path on the command line:
basis launch my_robot.launch.yaml --topic_prefix /sim/robot --enable_debug true

Conditional templating

Launch files support {% if %}, {% else if %}, and {% endif %} blocks:
---
args:
  include_foxglove_type:
    type: int32_t
    default: 0
---
{% if args.include_foxglove_type == 1 %}
include:
  foxglove.launch.yaml: {}
{% else if args.include_foxglove_type == 2 %}
include:
  - foxglove.launch.yaml: {}
{% endif %}
Expressions are also valid as YAML values:
groups:
  foxglove_inner:
    process: {{args.split_process}}
    units:
      foxglove: {}

Launch context

When basis launch runs, it populates a LaunchContext struct that governs process execution:
struct LaunchContext {
  bool sim = false;           // wait for simulated time on /time topic before starting
  std::string process_filter; // run only this named process (used in forked child launchers)
  std::vector<std::string> all_args;    // full original argv
  std::vector<std::string> launch_args; // arguments after the launch yaml path
};

CLI flags that map to LaunchContext

FlagFieldDescription
--simcontext.simBlock unit startup until a message arrives on the /time topic. Useful for simulation playback.
--process <name>context.process_filterRun only the named process group from the launch file. Used internally by forked child launchers.
--dry-run(no field)Parse and print the launch definition without starting any processes.
--mermaid(no field)Print a Mermaid diagram of the process/unit graph.

Simulation mode

When --sim is set, each child launcher waits on the /time topic before initializing units:
basis launch my_robot.launch.yaml --sim
The process will log "In simulation mode, waiting on /time" until a basis.core.transport.proto.Time message with a non-zero run_token is received.

Passing arguments to units in a launch file

Unit arguments are specified under args in the process definition:
processes:
  navigation:
    units:
      pathfinder: {}
      alt_pathfinder:
        unit: pathfinder       # reuse the same unit type with a different name
        args:
          use_experimental_algo: "1"
Each args entry is a key-value map of strings. The values are parsed according to the types declared in the unit’s .unit.yaml. If a required argument is missing or a value fails to parse, the launch will log a fatal error and stop.

Including launch files with arguments

One launch file can include another and pass arguments to it:
groups:
  foxglove:
    include:
      foxglove.launch.yaml:
        split_process: true
When including the same file more than once (for example, a camera launch file for two different cameras), use the sequence form:
include:
  - camera.launch.yaml:
      device: /dev/video0
      topic_namespace: /camera/front
  - camera.launch.yaml:
      device: /dev/video1
      topic_namespace: /camera/back
Use basis launch --dry-run my_robot.launch.yaml to verify argument substitution and launch file structure without starting any processes.

Build docs developers (and LLMs) love