Skip to content

Build a patrol robot in 10 minutes

This tutorial walks through the full cycle: clone the starter repo, define a world, write a patrol task, compile it, and run it in simulation. You will end up with a TurtleBot3 Burger navigating a set of named waypoints and reporting back to its dock.

What you need:

  • Docker (for the sim image)
  • Python 3.12+ with uv installed
  • defined CLI installed (pip install defined-cli)

1. Clone the starter repo

git clone https://github.com/Defined-Robotics/botcrate my-patrol-bot
cd my-patrol-bot

The repo ships with a working example called botcrate. You can run it immediately or adapt it.


2. Examine the default robot RDF

Open robots/burger.rdf.yaml:

robots/burger.rdf.yaml
name: turtlebot3_burger
version: "0.0.1"
description: >
  TurtleBot3 Burger differential-drive robot with 360-degree LiDAR.
  Supports navigation, exploration, and patrol tasks.

modules:
  - name: drive_base
    type: motor_controller
    capabilities:
      - name: differential_drive
        type: differential_drive
        parameters:
          wheel_separation: 0.160
          wheel_radius: 0.033
          max_linear_velocity: 0.22
          max_angular_velocity: 2.84

      - name: wheel_encoders
        type: wheel_encoders
        parameters:
          ticks_per_revolution: 4096

  - name: lidar_module
    type: sensor
    capabilities:
      - name: laser_scanner
        type: lidar_2d
        parameters:
          model: LDS-01
          range_min: 0.120
          range_max: 3.500
          samples: 360
          update_rate: 5.0
          topic: /scan

The RDF describes hardware capabilities, not software. The compiler reads required_capabilities on each verb and checks that the robot can satisfy them. go_to needs differential_drive and lidar_2d — both are present here.


3. Define POIs for your world

POIs (Points of Interest) are named locations in the environment. They appear in task files as $world.pois.<name> — the compiler resolves them at build time.

Open or create worlds/open_room.yaml:

worlds/open_room.yaml
name: room_10x10
description: >
  A 10m x 10m open room with perimeter walls only.
  Good for testing basic navigation without obstacles.

pois:
  dock:
    x: 0.0
    y: 0.0
    type: constant
    description: Home/charging station
  survey-1:
    x: 3.0
    y: 3.0
    type: static
    description: Northeast corner survey point
  survey-2:
    x: -3.0
    y: -3.0
    type: static
    description: Southwest corner survey point
  survey-3:
    x: 3.0
    y: -3.0
    type: static
    description: Southeast corner survey point

sim:
  environment: room_10x10
  spawn:
    x: 0.0
    y: 0.0
    yaw: 0.0

POI types:

type meaning
constant fixed for the lifetime of the robot (e.g. dock)
static changes rarely — loaded from world file
dynamic set at runtime by the robot or operator

4. Write the patrol task

Create tasks/patrol.task.yaml:

tasks/patrol.task.yaml
name: Patrol
description: "Visit survey points in sequence, report at each, return to dock"
steps:
  - verb: go_to
    params:
      target: $world.pois.survey-1
  - verb: report
    params:
      message: "arrived_survey_1"
  - verb: wait
    params:
      duration: 2.0
  - verb: go_to
    params:
      target: $world.pois.survey-2
  - verb: report
    params:
      message: "arrived_survey_2"
  - verb: wait
    params:
      duration: 2.0
  - verb: go_to
    params:
      target: $world.pois.survey-3
  - verb: report
    params:
      message: "arrived_survey_3"
  - verb: go_to
    params:
      target: $world.pois.dock
  - verb: report
    params:
      message: "patrol_complete"

Each step is a verb + params pair. The $world.pois.* references resolve to the x/y coordinates defined in your world file.


5. Set up defined.yaml

The project manifest tells the compiler which robot, world, and verb directory to use:

defined.yaml
project:
  name: my-patrol-bot

robot:
  rdf: robots/burger.rdf.yaml

world:
  file: worlds/open_room.yaml

verbs:
  path: verbs/

sim:
  image: ghcr.io/defined-robotics/sim:0.1.0

6. Compile and run in simulation

Compile the task:

defined compile tasks/patrol.task.yaml

The compiler outputs a BT XML file and validates that:

  • all verbs exist (built-in or local)
  • all $world.pois.* references exist in the world file
  • the robot has every required_capability the verbs need

Start the simulation and run the task:

defined run tasks/patrol.task.yaml

This pulls ghcr.io/defined-robotics/sim:0.1.0, spawns the robot at (0, 0), and launches the TUI.


7. Watch in the TUI

The defined run command opens a four-panel TUI:

┌─ Mission ──────────┐ ┌─ Robot State ──────┐
│ step 1/10          │ │ x:  0.000          │
│ go_to survey-1     │ │ y:  0.000          │
│ [running]          │ │ heading: 0.0°      │
└────────────────────┘ └────────────────────┘
┌─ Log ──────────────────────────────────────┐
│ [12:04:01] go_to → (3.0, 3.0)             │
│ [12:04:18] arrived_survey_1               │
│ [12:04:20] go_to → (-3.0, -3.0)          │
└─────────────────────────────────────────────┘
┌─ Command ─────────────────────────────────┐
│ > _                                        │
└─────────────────────────────────────────────┘

Reports published by the report verb appear in the Log panel as the robot reaches each waypoint.

Press q to stop the mission and return to the shell.


8. Modify and re-run

Try adding a fourth survey point. In worlds/open_room.yaml:

  survey-4:
    x: -3.0
    y: 3.0
    type: static
    description: Northwest corner survey point

Then add a step to tasks/patrol.task.yaml before the final go_to / report:

  - verb: go_to
    params:
      target: $world.pois.survey-4
  - verb: report
    params:
      message: "arrived_survey_4"

Re-compile and run:

defined compile tasks/patrol.task.yaml && defined run tasks/patrol.task.yaml

The compiler will catch any mismatch (missing POI, wrong capability) before the sim starts.


What's next

  • Add a camera to the robot and use capture_image at each stop — see the inspect.task.yaml example in tasks/.
  • Switch to the maze_10x10 world to test navigation in a confined space.
  • Write a custom verb — see Create your first custom verb.