This tutorial guides you through defining your first robot with Beam Bots. By the end, you'll understand the core DSL concepts and have a working robot definition.
Prerequisites
- Elixir 1.19 or later
- Beam Bots installed in your project
New to robotics? Robotics Vocabulary introduces links, joints, sensors, actuators and the rest of the terms used here, written for Elixir developers.
Quick Start with Igniter
The fastest way to get started is with Igniter. First, install the igniter_new archive if you haven't already:
mix archive.install hex igniter_new
Then create a new project with BB installed:
mix igniter.new my_robot --install bb
This creates a new Elixir project with a MyRobot.Robot module, adds it to your supervision tree, and configures the formatter. The generated module includes arm/disarm commands (covered in Commands and State Machine) and a base link — you can skip ahead to Step 2 and modify it.
If you already have an existing Elixir project, you can add BB to it instead:
mix igniter.install bb
Manual Installation
If you prefer to create the module manually, add Beam Bots to your dependencies:
# mix.exs
def deps do
[
{:bb, "~> 0.1"}
]
endWhat We're Building
We'll create a simple two-link robot arm: a base that can rotate (pan), with an arm that can tilt up and down. This is similar to a pan-tilt camera mount.
[camera] <- tilt joint rotates this
|
[pan_link] <- pan joint rotates this
|
[base_link] <- fixed to the worldStep 1: Create the Module
Create a new file lib/my_robot/robot.ex:
defmodule MyRobot.Robot do
use BB
topology do
link :base_link do
end
end
endLet's break this down:
use BBbrings in the Beam Bots DSL and the~usigil for physical unitstopology do ... enddefines the robot's physical structurelink :base_link do ... endcreates our first link (rigid body)
Compile and test:
iex> MyRobot.Robot.robot()
%BB.Robot{name: MyRobot.Robot, links: %{base_link: %BB.Robot.Link{...}}, ...}The robot/0 function returns a compiled struct optimised for runtime use.
Step 2: Add a Joint and Child Link
Joints connect links. Let's add a pan joint that allows rotation around the Z-axis:
defmodule MyRobot.Robot do
use BB
topology do
link :base_link do
joint :pan_joint do
type(:revolute)
axis do
end
# Revolute joints must declare their limits — Step 3 covers these.
limit effort: ~u(5 newton_meter), velocity: ~u(60 degree_per_second)
link :pan_link do
end
end
end
end
endKey concepts:
- Joints are nested inside links - the parent link contains the joint definition
- type(:revolute) - a revolute joint rotates around an axis (like a hinge)
- axis - defines which axis the joint rotates around. An empty axis block defaults to the Z-axis. You can specify different orientations using
roll,pitch, andyaw. - Child link is nested inside the joint - this creates the kinematic chain
For Roboticists: The DSL compiles to an Elixir struct at compile-time. There's no runtime parsing - the robot definition is baked into your module.
For Elixirists: Links are rigid bodies and joints are the connections between them that allow movement. Robotics Vocabulary walks through all six joint types with diagrams.
Step 3: Add Joint Limits
Real joints have physical constraints. Let's limit the pan joint's range of motion:
joint :pan_joint do
type(:revolute)
axis do
end
limit do
lower(~u(-90 degree))
upper(~u(90 degree))
effort(~u(5 newton_meter))
velocity(~u(60 degree_per_second))
end
link :pan_link do
end
endThe ~u() sigil creates unit-aware values:
~u(-90 degree)- negative 90 degrees~u(5 newton_meter)- maximum torque the joint can apply~u(60 degree_per_second)- maximum rotation speed
These units are automatically converted to SI base units (radians, newton-metres) in the compiled robot struct.
Step 4: Position the Joint
By default, joints are at the origin of their parent link. Use origin to offset them:
joint :pan_joint do
type(:revolute)
origin do
z(~u(0.05 meter))
end
axis do
end
limit do
lower(~u(-90 degree))
upper(~u(90 degree))
effort(~u(5 newton_meter))
velocity(~u(60 degree_per_second))
end
link :pan_link do
end
endThe joint is now 5cm above the base link's origin.
Step 5: Add a Second Joint
Let's add a tilt joint to create a full pan-tilt mechanism:
defmodule MyRobot.Robot do
use BB
topology do
link :base_link do
joint :pan_joint do
type(:revolute)
origin do
z(~u(0.05 meter))
end
axis do
end
limit do
lower(~u(-90 degree))
upper(~u(90 degree))
effort(~u(5 newton_meter))
velocity(~u(60 degree_per_second))
end
link :pan_link do
joint :tilt_joint do
type(:revolute)
origin do
z(~u(0.03 meter))
end
axis do
roll(~u(-90 degree))
end
limit do
lower(~u(-45 degree))
upper(~u(90 degree))
effort(~u(2 newton_meter))
velocity(~u(45 degree_per_second))
end
link :camera_link do
end
end
end
end
end
end
endThe tilt joint rotates around the Y-axis (specified by roll(~u(-90 degree)) which rotates the default Z-axis to point along Y), allowing the camera to look up and down.
Step 6: Add Visual Geometry
To visualise the robot, add visual geometry to each link:
link :base_link do
visual do
cylinder do
radius(~u(0.04 meter))
height(~u(0.05 meter))
end
material do
color do
red(0.2)
green(0.2)
blue(0.2)
alpha(1.0)
end
end
end
joint :pan_joint do
# ... joint definition
end
endAvailable geometry types:
box- withx,y,zdimensionscylinder- withradiusandheightsphere- withradiusmesh- withfilenamefor custom 3D models
Complete Example
Here's the full robot definition:
defmodule MyRobot.Robot do
use BB
topology do
link :base_link do
visual do
cylinder do
radius(~u(0.04 meter))
height(~u(0.05 meter))
end
material do
color do
red(0.2)
green(0.2)
blue(0.2)
alpha(1.0)
end
end
end
joint :pan_joint do
type(:revolute)
origin do
z(~u(0.05 meter))
end
axis do
end
limit do
lower(~u(-90 degree))
upper(~u(90 degree))
effort(~u(5 newton_meter))
velocity(~u(60 degree_per_second))
end
link :pan_link do
visual do
origin do
z(~u(0.015 meter))
end
box do
x(~u(0.03 meter))
y(~u(0.03 meter))
z(~u(0.03 meter))
end
material do
color do
red(0.3)
green(0.3)
blue(0.3)
alpha(1.0)
end
end
end
joint :tilt_joint do
type(:revolute)
origin do
z(~u(0.03 meter))
end
axis do
roll(~u(-90 degree))
end
limit do
lower(~u(-45 degree))
upper(~u(90 degree))
effort(~u(2 newton_meter))
velocity(~u(45 degree_per_second))
end
link :camera_link do
visual do
box do
x(~u(0.05 meter))
y(~u(0.03 meter))
z(~u(0.03 meter))
end
material do
color do
red(0.1)
green(0.1)
blue(0.1)
alpha(1.0)
end
end
end
end
end
end
end
end
end
endExploring the Compiled Robot
The robot/0 function returns a BB.Robot struct:
iex> robot = MyRobot.Robot.robot()
iex> Map.keys(robot.links)
[:base_link, :pan_link, :camera_link]
iex> Map.keys(robot.joints)
[:pan_joint, :tilt_joint]
iex> robot.joints.pan_joint.type
:revolute
iex> robot.joints.pan_joint.limits.upper
1.5707963267948966 # 90 degrees in radiansNotice that angles are stored in radians (SI units) even though we defined them in degrees.
Joint Types
BB supports six joint types:
| Type | Description | Use Case |
|---|---|---|
:revolute | Rotation with limits | Arm joints, pan-tilt |
:continuous | Unlimited rotation | Wheels |
:prismatic | Linear sliding | Linear actuators |
:fixed | No movement | Welded connections |
:floating | 6 degrees of freedom | Free-floating objects |
:planar | Movement in a plane | Some mobile bases |
What's Next?
You've defined a robot structure, but it's not running yet. In the next tutorial, we'll:
- Start the robot's supervision tree
- Understand how the process structure mirrors the physical structure
- Learn about Beam Bots' fault isolation model
Continue to Starting and Stopping.