Skip to content

Verb Authoring

A verb is the unit of robot behaviour in Defined Robotics. Each verb has two files: a YAML metadata file and a Jinja2 template that renders to BehaviorTree.CPP XML. Together they define what a verb requires from the robot, what parameters it accepts, and what BT nodes it produces.


Verb YAML structure

name: go_to
description: "Navigate the robot to a specified position"

# Capability gate — compilation fails if any of these are missing from the RDF
required_capabilities:
  - differential_drive
  - lidar_2d

# Jinja2 template file (in the same directory as this YAML)
template: go_to.xml.j2

# BT primitives this verb relies on (informational)
primitives:
  - navigate_to_pose

# Parameters passed to the Jinja2 template
parameters:
  x:
    type: float
    required: true
    description: "Target X coordinate (meters)"
  y:
    type: float
    required: true
    description: "Target Y coordinate (meters)"
  theta:
    type: float
    required: false
    default: 0.0
    description: "Target orientation (radians)"
  timeout:
    type: float
    required: false
    default: 60.0
    description: "Navigation timeout in seconds"

# Output parameters emitted by the BT node (informational)
output_parameters:
  error_code:
    type: int
    description: "Navigation error code"

# ROS dependencies needed at runtime
dependencies:
  apt:
    - ros-jazzy-nav2-bringup
  source:
    - repo: https://github.com/Defined-Robotics/defined-platform
      ref: trunk
      packages: [defined_runtime]

# Runtime node configuration
runtime:
  nodes:
    - package: nav2_bringup
      executable: navigation_launch.py
      parameters:
        use_sim_time: true

# ROS interface contract
interface:
  actions:
    - name: /navigate_to_pose
  topics:
    subscribes: []
    publishes: []

Parameter fields

Field Type Required Description
type string yes float, int, string, or bool
required bool yes Whether the caller must supply this parameter
default any no Default value when parameter is omitted (required if required: false)
description string no Human-readable description

Jinja2 template format

The template file renders to one or more BT XML nodes. Parameters are available as Jinja2 variables.

{# go_to.xml.j2 #}
<RetryUntilSuccessful num_attempts="{{ retries | default('3') }}">
    <Action ID="GoTo" name="go_to_{{ x }}_{{ y }}"
        x="{{ x }}" y="{{ y }}" theta="{{ theta | default('0.0') }}"
        frame_id="{{ frame_id | default('map') }}"
        server_name="{{ server_name | default('/navigate_to_pose') }}"
        timeout="{{ timeout | default('60.0') }}" />
</RetryUntilSuccessful>
{# explore.xml.j2 #}
<Action ID="Explore" timeout="{{ timeout | default('300.0') }}"/>

Standard Jinja2 filters work — | default(...), | upper, | int, etc. The template receives all parameters from the task step's params dict, with missing optional parameters substituted by their defaults before rendering.


Built-in verbs

Verb Required capabilities Parameters Notes
go_to differential_drive, lidar_2d x (req), y (req), theta (0.0), timeout (60.0), frame_id ("map"), server_name ("/navigate_to_pose"), retries (3) Wraps in RetryUntilSuccessful
explore differential_drive, lidar_2d timeout (300.0) Runs frontier exploration
report message ("checkpoint"), topic ("/task_reports"), level ("info") Publishes to ROS topic
wait duration (req) Pauses execution
capture_image rgb_camera topic ("/camera/image_raw"), save_path ("/tmp/capture"), timeout (5.0) Saves image to disk

Adding a new verb

Step 1. Create the verb directory (or use an existing verbs/ directory):

mkdir -p verbs/

Step 2. Write the verb YAML (verbs/take_photo.yaml):

name: take_photo
description: "Capture a photo and save to disk"
required_capabilities:
  - rgb_camera
template: take_photo.xml.j2
primitives:
  - capture_image
parameters:
  save_path:
    type: string
    required: false
    default: "/tmp/photos"
    description: "Directory to save images"
  topic:
    type: string
    required: false
    default: "/camera/image_raw"
    description: "Camera topic"
output_parameters:
  image_path:
    type: string
    description: "Path to saved image"
dependencies:
  apt:
    - ros-jazzy-cv-bridge
  source:
    - repo: https://github.com/Defined-Robotics/defined-platform
      ref: trunk
      packages: [defined_runtime]
runtime: {}
interface:
  topics:
    subscribes:
      - /camera/image_raw

Step 3. Write the Jinja2 template (verbs/take_photo.xml.j2):

<Action ID="CaptureImage"
    topic="{{ topic | default('/camera/image_raw') }}"
    save_path="{{ save_path | default('/tmp/photos') }}" />

Step 4. Use the verb in a task:

name: PhotoPatrol
steps:
  - verb: go_to
    params:
      x: 2.0
      y: 1.0
  - verb: take_photo
    params:
      save_path: "/data/patrol_photos"
  - verb: report
    params:
      message: "photo_captured"

Step 5. Compile with your verbs directory:

defined compile tasks/photo_patrol.task.yaml \
    --rdf robot.rdf.yaml \
    --verbs-dir verbs/ \
    -o photo_patrol.bt.xml

Capability validation

At compile time, each verb's required_capabilities list is checked against the capabilities declared in the robot's RDF. If any capability is missing, compilation aborts:

REJECTED: verb 'take_photo' requires capabilities ['rgb_camera']
not available on robot 'my_robot'

To add the capability to your robot, add a module with that capability type to your RDF:

modules:
  - name: camera_module
    type: camera
    capabilities:
      - name: vision
        type: rgb_camera
        parameters:
          resolution: [640, 480]
          fps: 30

Any string is a valid capability type. Custom verbs can require any type you define — the compiler does a simple string match.