Getting started
This guide takes the demo robot from four source files to a run in Gazebo: a mecanum base with a lidar that drives around a box to a goal, turns a quarter turn and stops. Its four files are in examples/demo and its parts in examples/library, so it runs from a checkout of the repository, which is not public yet (Installation).
The program
The four files are in examples/demo; the tabs show one each.
// The demo robot: a mecanum base with a battery and two motor drivers on its deck, a motor
// on each drive mount with its wheel on the shaft, and a top lidar.
Robot drone_mecanum {
intent "Drone mecanum mobile base with four black mecanum wheels and a top lidar"
Mecanum_four_wheel chassis(ref = "tslot_1515_drone")
Battery pack(ref = "lipo_3s_2200", glue = (-0.1, 0, 0.0344, 0, 0, -1))
MotorDriver driver_l(ref = "driver_vnh5019", glue = (0, 0.1, 0.0344, 0, 0, -1), power = @core/pack)
MotorDriver driver_r(ref = "driver_vnh5019", glue = (0, -0.1, 0.0344, 0, 0, -1), power = @core/pack)
Motor motor_lf(ref = "gearmotor_37d", glue = (0.1275, 0.1294, -0.0216, 0, 1, 0), command = @core/driver_l)
Motor motor_lb(ref = "gearmotor_37d", glue = (-0.1275, 0.1294, -0.0216, 0, 1, 0), command = @core/driver_l)
Motor motor_rf(ref = "gearmotor_37d", glue = (0.1275, -0.1294, -0.0216, 0, -1, 0), command = @core/driver_r)
Motor motor_rb(ref = "gearmotor_37d", glue = (-0.1275, -0.1294, -0.0216, 0, -1, 0), command = @core/driver_r)
Mecanum wheel_lf(ref = "mecanum_black", glue = (0.1275, 0.1494, -0.0216, 0, 1, 0))
Mecanum wheel_lb(ref = "mecanum_black", glue = (-0.1275, 0.1494, -0.0216, 0, 1, 0))
Mecanum wheel_rf(ref = "mecanum_black", glue = (0.1275, -0.1494, -0.0216, 0, -1, 0))
Mecanum wheel_rb(ref = "mecanum_black", glue = (-0.1275, -0.1494, -0.0216, 0, -1, 0))
Lidar top_lidar(ref = "lidar_ld06", glue = (0, 0.0001, 0.0344, 0, 0, -1))
}World demo {
physics(gravity = (0, 0, -9.81))
spawn(robot = @core/drone_mecanum, pose = (0, 0, 0.05, 0, 0, 0, 1))
Scenario room(ref = "empty", ground_plane = true) {
Obstacle box(
type = "box", pos = (1.2, 0, 0.25, 0, 0, 0, 1), size = (0.3, 0.3, 0.5),
),
Obstacle wall_north(
type = "box", pos = (2, 5, 0.25, 0, 0, 0, 1), size = (8.2, 0.1, 0.5),
),
}
Goal goal(pose = (2.5, 0, 0, 0, 0, 0, 1))
}Task drone_demo(timeout_s = 120) {
Retry(count = 2) {
Navigate(goal = @world/goal)
}
Sequence {
Spin(angle_rad = 1.57),
Wait(duration_s = 2),
Stop()
}
}Evaluate demo_eval {
Check_clearance(target = @world/goal, min = 0.3)
Check_vel(target = @core/wheel_lf, min = 0, max = 60)
Check_time(target = @task/drone_demo, max = 300)
}The robot is a chassis part and twelve parts glued onto it: a battery and two motor driver boards on the deck, a motor on each of the four drive mounts, a wheel on each motor's shaft, and a lidar on top. Each glue is the position and direction of a connector, of the chassis or of a part already placed. Each driver's power names the battery that feeds it, and each motor's command the driver that drives it. The world has two obstacles and a goal. The task navigates to the goal, retrying once, then spins, waits and stops. The evaluation asks for 0.3 m of clearance, a wheel speed under 60 rad/s and a task under 300 s.
Check it
From the repository root, --check runs every phase up to validation and writes nothing:
mobiledsl compile examples/demo/demo.* --library examples/library --checkChecked examples/demo/demo.core examples/demo/demo.eval examples/demo/demo.task examples/demo/demo.world (14 checks)The fourteen validation rules that apply to this robot passed. Change Lidar to Lidra in demo.core and run it again to see what a problem looks like:
error[name.unknown_component_type]: unknown component type 'Lidra'
--> examples/demo/demo.core:22:5
|
22 | Lidra top_lidar(ref = "lidar_ld06", glue = (0, 0.0001, 0.0344, 0, 0, -1))
| ^^^^^ not a component type
|
= help: did you mean 'Lidar'?
error: 1 error; examples/demo/demo.core examples/demo/demo.eval examples/demo/demo.task examples/demo/demo.world did not compileCompile it
Name a target and an output directory:
mobiledsl compile examples/demo/demo.* --library examples/library \
--target ros-gz-nav2 --output buildCompiled examples/demo/demo.core examples/demo/demo.eval examples/demo/demo.task examples/demo/demo.world (14 checks) into build (40 files)build/ is the whole robot stack's input:
build/
├── robot/ robot.urdf, controllers.yaml, meshes
├── world/ world.sdf, bridge.yaml
├── nav/ Nav2 parameters and the map drawn from the obstacles
├── task/ behavior_tree.xml, mission.json, evaluation.json
├── launch/ bringup.launch.py, the entry point
├── runtime/ the task runner, which needs only ROS 2
└── compile.json what the compiler knew, for people and toolsRun it
In a ROS 2 Jazzy environment with Gazebo Harmonic, ros2_control and Nav2:
ros2 launch build/launch/bringup.launch.pyThe launch starts Gazebo without a window, spawns the robot, starts its controllers and Nav2, and runs the task. When the task ends it shuts everything down. Add headless:=false to watch the run in Gazebo's window.
Without such an environment, run it in the test image. Mount the output and launch it inside:
docker run --rm --user "$(id -u):$(id -g)" -e HOME=/tmp -e GZ_PARTITION=robotdsl \
-v "$PWD/build:/work/build" --entrypoint bash robotdsl/ros-gz-nav2:jazzy-harmonic \
-lc "source /opt/ros/jazzy/setup.bash && ros2 launch /work/build/launch/bringup.launch.py"The container runs as you, so the results it writes are yours. Gazebo names its transport partition after the user, and the image does not know your user, so GZ_PARTITION gives it a name; without it the robot never spawns. --entrypoint bash replaces the image's own entry point.
Read the result
The run writes two files under build/run/:
| File | Says |
|---|---|
task_result.json | The task's own account: success, failure, timeout, error or not_started, and one event per action with its times and outcome |
evaluation.json | Three verdicts: the task's status, the physical outcome judged from the simulator's own pose, and each check of demo.eval with the value it was judged on |
A successful demo run navigates around the box, stops near the goal and never touches the box. Its clearance check can still fail: Nav2 may pass the box closer than the 0.3 m the evaluation asks for, and the check reports how close it came. Judging a run explains the three verdicts.
Next
The Basics pages take each file in turn:
- The robot: chassis, parts and
glue - The world: spawn, obstacles and goals
- The task: actions and control flow
- The evaluation: checks and bounds