Skip to main content
A unit YAML file describes the behavior of a single Basis unit: its argument interface, threading model, and the set of handlers that subscribe to inputs and publish outputs. The file name convention is <unit_name>.unit.yaml.

Top-level fields

args
object
A map of argument names to their definitions. Arguments are passed to the unit at launch time via key=value pairs and are code-generated into typed C++ fields on the unit class.
cpp_includes
string[]
A list of additional C++ #include paths to inject into the generated unit header. Useful when your handler types reference custom message types not automatically included by the serializer.
cpp_includes:
  - my_project/custom_types.h
  - third_party/some_lib.h
threading_model
string
Controls how handlers are allowed to execute relative to each other.
ValueBehavior
singleAll handlers run mutually exclusive from each other (default).
handlers
object
required
A map of handler names to handler definitions. Each handler is a function invoked when its synchronization condition and inputs are satisfied. See Handler definition below.

Handler definition

Each key under handlers is a user-chosen handler name (e.g. StereoMatch, OnLidarSync). The value is an object with the following fields.
sync
object
Controls when the handler fires. Omitting sync means the handler never fires automatically.
The sync value external is also valid. Use it when the handler is triggered externally (i.e., the unit pushes data out on its own, not in response to incoming messages).
inputs
object
A map of topic paths to input definitions. All non-optional inputs must be satisfied (according to sync.type) before the handler fires. See Input definition below.
outputs
object
A map of topic paths to output definitions. These are the topics the handler is expected to publish to. See Output definition below.

Input definition

Each key under inputs is a topic path (e.g. /camera_left). The value is an object combining the common transport fields below with input-specific fields.
type
string
required
The message type in serializer:MessageType format.
rosmsg:sensor_msgs::Image
protobuf:StampedVectorData
The serializer prefix tells Basis which deserialization path to use. Common prefixes are rosmsg and protobuf.
inproc_type
string
An alternate type to use when the message is delivered over the in-process (inproc) transport. Useful when the in-process representation differs from the wire format.
sync_field
string
The field or method to call on the message to obtain the synchronization key. Used with equal and approximate sync types.
PatternC++ expression produced
::fieldmsg->field
::method_fieldmsg->method_field()
complex.access.to_string()[](T* msg){ return msg->complex.access.to_string(); }
sync_field: header.stamp()        # ROS message header stamp
sync_field: ::time                # raw double field on protobuf message
sync_field: header.stamp.toSecs() # converted to seconds
accumulate
integer
Accumulate this many messages before passing them to the handler as a batch. Set to a large integer or use true for infinite accumulation. Useful for event-style inputs that arrive at a higher rate than the sync period.
cached
boolean
When true, the last received message is retained between handler invocations and re-used when no new message has arrived.
optional
boolean
When true, the handler may fire even if no message has arrived on this topic. The argument in the generated handler function will be a nullable/optional pointer.
allow_transports
string[]
Whitelist of transport names this input is permitted to receive from. Mutually exclusive with deny_transports.
allow_transports:
  - inproc
deny_transports
string[]
Blacklist of transport names this input must not receive from. Mutually exclusive with allow_transports.
deny_transports:
  - inproc
qos
object
Quality-of-service settings for this input.

Output definition

Each key under outputs is a topic path (e.g. /camera_stereo). Output definitions share the transport and QoS fields with inputs, plus one additional field.
type
string
required
The message type in serializer:MessageType format. Same format as input type.
inproc_type
string
Alternate type for in-process delivery.
optional
boolean
When true, the handler is not required to publish on this topic every invocation. By default all declared outputs are expected on every call; omitting a non-optional output raises a diagnostic error.
allow_transports
string[]
Whitelist of transports this output may use. Mutually exclusive with deny_transports.
deny_transports
string[]
Blacklist of transports this output must not use. Mutually exclusive with allow_transports.
qos
object

Complete example

The example below covers all handler patterns from the Basis example unit.
# All handlers in this unit are mutually exclusive
threading_model: single

handlers:
  # Equal-timestamp sync: fires when both cameras have a frame with the same header.stamp()
  StereoMatch:
    sync:
      type: equal
      buffer_size: 2
    inputs:
      /camera_left:
        type: rosmsg:sensor_msgs::Image
        sync_field: header.stamp()
      /camera_right:
        type: rosmsg:sensor_msgs::Image
        sync_field: header.stamp()
    outputs:
      /camera_stereo:
        type: rosmsg:example_msgs::StereoImage

  # Approximate sync: fires when lidar and vector timestamps are within 5ms;
  # accumulates up to 10 optional event messages while waiting
  OnLidarSync:
    sync:
      type:
        approximate: 5ms
      buffer_size: 5
    inputs:
      /lidar_data:
        type: rosmsg:sensor_msgs::PointCloud2
        sync_field: header.stamp.toSecs()
      /vector_data:
        type: protobuf:StampedVectorData
        sync_field: ::time
      /event_data:
        type: protobuf:Event
        optional: true
        accumulate: 10
    outputs:
      /lidar_sync_event:
        type: protobuf:LidarSyncEvent

  # Rate-based: fires at 10 Hz; /topic_a is required, /topic_b is optional
  TenHertzWithTopic:
    sync:
      rate: 10hz
    inputs:
      /topic_a:
        type: protobuf:A
        allow_transports:
          - inproc
      /topic_b:
        type: protobuf:B
        optional: true
        deny_transports:
          - inproc
    outputs:
      /topic_c:
        type: protobuf:C
        optional: true

  # All-inputs sync: fires once every listed input has been received at least once
  HandleAll:
    sync:
      type: all
    inputs:
      /topic_c:
        type: protobuf:C
      /topic_d:
        type: protobuf:D

  # External: unit publishes on its own schedule, not driven by incoming messages
  External:
    sync:
      external
    outputs:
      /some_image:
        type: rosmsg:sensor_msgs::Image

Handler type summary

All inputs must carry the same sync_field value. The handler fires once per matching set. Use buffer_size to control how many unmatched messages are held waiting for a partner.
sync:
  type: equal
  buffer_size: 2
All inputs must carry sync_field values within the specified tolerance of each other. The tolerance value is a duration (e.g. 5ms).
sync:
  type:
    approximate: 5ms
The handler fires as soon as every non-optional input has at least one queued message. Order of arrival does not matter.
sync:
  type: all
The handler fires on a wall-clock timer. Any declared inputs are passed through when available but do not gate the timer. Specify the frequency in Hz.
sync:
  rate: 10hz
The unit publishes to the declared outputs on its own schedule (e.g. from a driver callback). No incoming messages are required.
sync:
  external

Build docs developers (and LLMs) love