25.2
Quickstart

Quickstart

This quickstart is a collection of basic Wandelscript syntax elements with examples.

Indentation

When using Wandelscript, indentation is crucial. Here are the main rules that apply for Wandelscript:

The indentation level is used to define the scope of a block of code.

do with ur10[0]
  move frame("Flange") via p2p() to (50, 20, 30, 0, pi, 0)

We recommend an indentation level of 4 spaces. The amount of spaces is up to you, but it has to be at least one.

do with ur10[0]
    move frame("Flange") via p2p() to (50, 20, 30, 0, pi, 0)

You have to use the same amount of indentation when nesting blocks of code.

do with ur10[0]:
    for i = 0..5:
        move frame("Flange") via p2p() to (50, 20, 30, 0, pi, 0)
 
# This will throw an error
 
do with ur10[0]:
    for i = 0..5:
      move frame("Flange") via p2p() to (50, 20, 30, 0, pi, 0)

As Wandelscript uses an indentation style similar to Python, we recommend this article (opens in a new tab) for further details on Python indentation.

Comments

Comments are declared with a # as a single line until the end of the line.

# This is a comment

Execution order

Wandelscript execution happens step-by-step, consecutively.

home = (-153.3, -538.1, 454.9, 3.068, -0.623, -0.13)
move via p2p() to home
wait(500)
    print("Home reached.")

First, home is declared, second the robot moves to home, and after 500 milliseconds "Home reached." is printed.

There are a couple of exceptions and commands that influence the execution order. Read more about them here.

Precedence

Wandelscript precedence is determined from left to right on the same level. As per usual you can use parentheses () to enforce a specific order of precedence.

The following table lists the order of precedence from highest to lowest.

Level of precedenceDescription
()Parentheses, overrules precedence from left to right for operators on the same level
+n, -n, ~n, notUnary plus, unary minus, inverse, logical not
*, /, ::Multiplication, division, pose concatenation
+, -Addition, subtraction
>, <, ==, <=, >=, !=Comparison expressed by booleans
and, orLogical and, logical or

Learn more about which operators exist in Wandelscript further down the page or skip to the table right now.

Entry point

An entry point of a Wandelscript program is declaring the home pose of the robot. The home pose acts as a reference point for a robot to return to before or after executing a movement.

home = (139.9, -37.3, 319.8, -0.9, -2.5, -1.1)

Print to the standard output

print prints its argument to the standard output:

print("hello world")

Wait

wait() waits for the indicated amount of time in milliseconds:

wait(1000)

Functions

Wandelscript functions are declared using the def keyword.:

def <function_name>(<parameters>):
    <code_block>

Separate parameters by commas. Types are not supported in Wandelscript.

def function(value1, value2):

More on how functions can be used as extensions can be found here.

get_controller

To easily identify different controllers, you can use the get_controller function:
<variable> = get_controller("controller_id")

fluffy_cat = get_controller("ur-controller")
 
do with fluffy_cat:
    move frame("Flange") via p2p() to (100, 200, 300, 0, pi, 0)

More information on how to apply get_controller can be found here.

Variables

In Wandelscript, you declare a variable by assigning a value to it with =:

pose = (100, 200, 300, 0, pi, 0)

- and . are not allowed in variable names.

When assigning variables, you can specify data types supported by Wandelscript. Here's a selection of the most popular data types, go to Data types for all supported data types.

Primitives

str_val = 'Hello, World!'
int_val = 20
float_val = 20.5
bool_val = True

Pose

# x, y, z, rx, ry, rz
pose = (100, 200, 300, 0, pi, 0)

Lists

a = [1, 2, 3, 4, [5, 6]]
b = a[2] # 3
c = a[4] # [5, 6]
d = c[1] # 6
e = c[1] # 6

Read pose and joint values from robot

As often required during programming, you can read the current pose of the robot with:

current_pose = read(robot, 'pose')

and joint values with:

current_joints = read(robot, 'joints')

Operators

Unary

OperatorNameDescriptionExample
-nSignThe variable b is assigned with negation of a. In other words stem:b = -1 * a.b = -a
~InverseThe inverse operator calculates the inverse of a pose.pose_b2a = ~pose_a2b
notLogical notReturns boolean values true for falsy values and false for truthy values.a = True
b = ~a
c = ~b
d = not c
e = not d
print(a, b, c, d, e)
returns
a=True, b=False, c=True, d=False, e=True

Binary

OperatorNameDescriptionExample
+AdditionAdd two numbers and assign them to the variable a.a = 4 + 5
-SubtractionSubtract two numbers and assign them to the variable b.b = 4 - 1
*MultiplicationMultiply two numbers and assign them to the variable c.c = 10 * 2
/DivisionDivide two numbers and assign them to the variable d. When divided by 0 the Wandelengine will throw an error.d = 4 / 2
::Pose concatenationConcatenation of two poses and assigning them to a new variable, e.g. tcp2robot. Given three frames, e.g. tcp, object, and robot, and two frame relations as poses, e.g., tcp2object and object2robot, the relation tcp2robot can be obtained by the pose concatenation. If 2 poses are concatenated, the operator returns a pose. If one or both of the operants are positions, it returns a position. More on pose concatenation here.tcp2robot = object2robot :: tcp2object
>, <, ==, <=, >=, !=ComparisonWandelscript supports the most used boolean expressions for comparisons.Lower: a < b
Greater: a > b
Lower equal: a <= b
Greater equal: a >= b
Equal: a == b
Not equal: a != b
andLogical andVerifies if all values are truthy. If yes, returns the last truthy value. If not, returns the first falsy value.All are truthy: 2 and 5 returns 5
At least one is falsy: 0 and 2 returns 0
orLogical orVerifies if one of the values is truthy. If one of them is truthy, it returns the truthy value. If both of them are truthy, it returns the first truthy value. If both of them are falsy, it returns the last falsy value.One is truthy: 0 or 5 returns 5
Both are truthy: 2 or 5 returns 2
Both are falsy: 0 or False returns False

Range

OperatorNameDescriptionExample
..Range including last elementCreate a range from a to b that does include b.for a = 1..4: # a = will be 1, 2, 3, and 4
..<Range excluding last elementCreate a range from a to b that does not include b.for a = 1..<4: # a = will be 1, 2, and 3

Control Flow

Control flow statements are used to control the flow of execution in a program's script. Wandelscript supports the following control flow statements:

sync

sync synchronizes all previously executed commands regardless of indentation.

move frame("Flange") via p2p() to (150, -355, 389, 0, 0, 0)
move frame("Flange") via line() to (-95, -363, 387)
sync
write(io, "digital_out[7]", True)

With sync, the I/O digital_out[7] is updated after all movements are finished thus ensuring that the step-by-step execution is maintained.

Read more on synchronization here.

If Else

if is used to run a code block only when a certain condition is met.

Add more conditions with elif. Use else to define items to be executed when non of the if conditions were met.

a = 10
if a == 10:
    print("a is 10")
elif a == 20:
    print("a is 20")
else:
    print("a is not 10 or 20")

Switch

switch is syntactic sugar for an if...elif...else, e.g. checking a variable for a specific value. default is required when using switch.

s = "c"
b = 0
switch s:
case "a": b = 2
case "b": b = 3
case "c": b = 4
case "c": b = 5
default: b = 6

for loops

for executes specific iterations until a specific condition has been met.

c = 0
arr = [1, 2, 3]
for i = 0 ..< len(arr):
    c = c + arr[i]

Range Operator:

for i = 1..<4:
    print(i)  # 1, 2, 3
for i = 1..4:
    print(i)  # 1, 2, 3, 4

While

While executes the defined code block repeatedly as long as the defined condition is met.

i = 0
while i < 10:
    i = i + 1

To create an infinite loop, use while True:. Remember to add an explicit sync to end the loop.

Movement commands

move is a first-class citizen command that describes a robot movement by defining the desired <tcp>, the movement's motion type <connector> and target pose <pose> or joint configuration <joint values>:

move <tcp> via <connector> to <pose>
 
move <tcp> via <connector> to <joint values>

Set TCP

Set the TCP to use for the movement by using the TCP's coordinate system with frame, or by using tcp:

move frame("<tcp_id>") via p2p() to (100, 200, 300, 0, pi, 0)
 
move tcp("<tcp_id>") via joint_p2p() to [1.890, -1.135, 2.5690, -1.134, 1.8912, 1.28]

More information on how to get available TCPs here. In this case, Flange is one of the available TCPs:

move frame("Flange") via p2p() to (100, 200, 300, 0, pi, 0)
 
move tcp("Flange") via joint_p2p() to [1.890, -1.135, 2.5690, -1.134, 1.8912, 1.28]

Set connector

The following <connectors> are available to specify the motion type of the movement:

Point to point (P2P)

p2p: The fastest possible motion from A to B with cartesian movement. Depends on robot and current joint configuration. The robot's end joint configuration can differ from the starting joint configuration.

Indicate the target pose with a pose with cartesian values.

pose = (100, 200, 300, 0, pi, 0)
move frame("Flange") via p2p() to pose

Modify the target pose at runtime with expressions.

Linear

line: A linear motion from pose A to B. The robot stays exactly on the linear path between A and B.

pose = (100, 200, 300, 0, pi, 0)
move frame("Flange") via line() to pose

Modify the target pose at runtime with expressions.

Circular

arc: A circular motion from A to B over a point C.

pose = (100, 200, 300, 0, pi, 0)
move frame("Flange") via arc(150, 200, 300, 0, pi, 0) to pose

Modify the target pose at runtime with expressions.

Joint point to point (Joint P2P)

joint_p2p: The shortest possible motion from A to B optimized for the robot movement. Not necessarily a straight line from A to B as the robot avoids reaching its joint limits and thus blocking its movements. Ensures that the robot ends in the correct joint configuration.

Indicate the target joint configuration with a list of joint values, using . for decimal values.

joints = [1.890, -1.135, 2.5690, -1.134, 1.8912, 1.28]
move frame("Flange") via joint_p2p() to joints

Relative to a position

Sometimes you want to move the robot relative to a certain position. You can achieve this with the :: operator to concatenate the current pose with the relative pose. In the following example the robot will move 50mm in z direction in the flange coordinate system.

pose = (100, 200, 300, 0, pi, 0)
# move 50mm in z direction in the flange coordinate system
move frame("Flange") via p2p() to pose :: (0, 0, 50)

Remember, the order of the poses is important. In the following example the TCP will move in the robot coordinate system, 50mm in z direction.

pose = (100, 200, 300, 0, pi, 0)
# move 50mm in z direction in the robot coordinate system
move frame("Flange") via p2p() to (0, 0, 50, 0, pi, 0) :: pose

Modify the target pose at runtime with expressions.

Execute movements

To execute a movement on the robot, place a sync after the move command. If you group multiple movements together, they will get bundled. The sync ensures that they are recognized as one movement group and Wandelscript written afterwards is executed once the movements have been completed.

move frame("Flange") via p2p() to (150, -355, 389, 0, 0, 0)
move frame("Flange") via line() to (-95, -363, 387)
sync
print("Movement completed")

More on how to use sync with movements can be found here.

Movement settings

The movement settings supported by Wandelscript are velocity, acceleration, indicated in milliseconds, and blending, indicated in millimeters.

velocity

Set a target pose and the velocity of the movement to 100 ms.

pose = (100, 200, 300, 0, pi, 0)
move frame("Flange") via p2p() to pose with velocity(100)

acceleration

Set a target pose and the acceleration of the movement to 100 ms.

pose = (100, 200, 300, 0, pi, 0)
move frame("Flange") via p2p() to pose with acceleration(100)

blending

blending sets the blending radius in millimeters.

Let's say you want to reach home from the current robot position via pose, blending of the movement set to 20 mm.

pose = (100, 200, 300, 0, pi, 0)
move frame("Flange") via p2p() to pose with blending(20)
move frame("Flange") to home

Imagine two lines between A = the current position of the robot, B = pose and C = home.
The robot moves from A over B = pose to C = home.

The indicated blending towards B means that the robot will not reach pose but blend within the indicated blending zone r = 20 millimeters.

Add robot to cell endpoint and select the desired robot from the examples dropdown
Add robot to cell endpoint and select the desired robot from the examples dropdown

Movement scope

To apply movement commands or settings to a motion, set them globally or using with for inline and block scopes.

During execution, introduced motion settings and movement commands are respected in the following order:

  1. Inline
  2. Block scope
  3. Global

The more precise the settings are, the higher the priority. More on that later here.

Inline

Use with to set the desired TCP and movement settings inline:

move via p2p() to (100, 200, 300, 0, pi, 0) with tcp("Flange"), blending(20), velocity(120)

or as displayed above, you can set the desired TCP by referencing the coordinate system with frame:

move frame("Flange") via p2p() to (100, 200, 300, 0, pi, 0) with blending(20), velocity(120)

Block

To apply movement settings and commands for multiple movements within a block of code, apply the settings in a block scope using with.

pose1 = (150, -355, 389, 0, 0, 0)
pose2 = (223.0, 16.6, 779.7, 1.0758, 2.9742, -0.0871)
 
with velocity(250), blending(5), tcp("Flange"):
    move via p2p() to pose1
    move via line() to pose2

Global

To apply movement settings and commands for an entire file, apply the settings in a global scope at the beginning of the file. All following moves wil be executed with the flange TCP with a velocity of 250 ms and an acceleration of 30 until the settings are changed:

tcp("Flange") #Set the TCP to use for the whole Wandelscript file
velocity(250)
acceleration(30)
 
home= (100, 200, 300, 0, pi, 0)
 
move via p2p() to (223.0, 16.6, 779.7, 1.0758, 2.9742, -0.0871)
move via p2p() to home

If you prefer to use the frame function to set the TCP, you can do so globally as well.

flange = frame("Flange")
tcp(flange) #Set the TCP to use for the whole Wandelscript file
velocity(250)
acceleration(30)
 
move via p2p() to (100, 200, 300, 0, pi, 0)

Changing TCPs

When combining inline, block, and global settings, the inline settings have the highest priority, followed by block settings, and finally global settings. Use this to your advantage and figure out how to scarcely set the settings to achieve lean code. Play around with the following examples to understand how precedence, scope and TCP settings work and lead to different movement executions.

We'll start by setting a global TCP and further down, an inline TCP. The first movement via joint p2p will be executed with Flange. The second movement via line will be executed with Gripper. Notice how after each movement there's an explicit sync to mark the end of the motion chain. Also, the first movement command is executed slower than the second one; inline velocity overruling the global velocity.

flange = frame("Flange")
tcp(flange)
velocity(250)
 
home = (100, 200, 300, 0, pi, 0)
 
move via p2p() to (223.0, 16.6, 779.7, 1.0758, 2.9742, -0.0871) with velocity(50)
sync
print("Reached home.")
 
move frame("Gripper") via p2p() to home
sync
print("Left home.")

Try leaving the sync out.

flange = frame("Flange")
tcp(flange)
velocity(250)
 
home = (100, 200, 300, 0, pi, 0)
 
move via p2p() to (223.0, 16.6, 779.7, 1.0758, 2.9742, -0.0871) with velocity(50)
move frame("Gripper") via p2p() to home

As TCPs can't be changed within one motion chain, the movement fails. Without a sync, both move commands are considered part of a block. More on explicit sync here.

Now what about with? Same behavior here, changing the TCP within a block is only possible if an explicit sync is used to mark the end of the first motion chain. Notice how the block blending overrules the global blending.

flange = frame("Flange")
tcp(flange)
velocity(250)
blending(10)
 
home = (100, 200, 300, 0, pi, 0)
 
with blending(0):
    move via p2p() to (223.0, 16.6, 779.7, 1.0758, 2.9742, -0.0871)
    move via p2p() to home
    sync
    move via p2p() to (223.0, 16.6, 779.7, 1.0758, 2.9742, -0.0871) with tcp("Gripper")

One robot vs. multiple robots

default_robot is automatically set when programming with one robot in a cell. This robot is used to execute all commands.

When programming with multiple robots in a cell, you can set one robot as the default robot in a cell by using the POST: /programs/runners or POST: /programs/execute endpoints.

{
  "code": "...",
  "default_robot": "robot_name"
}

Robot context

do with <robot>: is used to specify which robot should execute the following commands and acts as a implicit sync.

Programs without the use of robot context do with <robot>: are always executed with the default robot:

# without robot context
home = (...)
move frame("Flange") via p2p() to home
move frame("Flange") via line() to home :: (...)

If you're executing a programm without robot context and without a default robot, you'll get prompted to use robot context do with <robot>:. A program with only a robot context looks like this:

# using robot context
do with ur10e[0]:
    move frame("Flange") via p2p() to home
and do with ur5e[0]:
    move frame("Flange") via p2p() to home

As Wandelscript desires you to be as precise as possible when programming multiple robots for safety reasons, mixing default_robot and robot context do with <robot>: is not allowed.

# executed by the default robot
flange = frame("Flange")
move via p2p() to home
 
# with robot context
do with ur10e[0]:
    move via p2p() to home
and do with ur5e[0]:
    move via p2p() to home2

More information on robot context with multiple robots is available here.

Interact with other cell objects

There are three ways to interact with other, non-robot cell objects in Wandelscript:

  1. Reading from the device with val = read(<identifer>, <key>)
  2. Writing to the device with write(<identifier>, <key>, <value>)
  3. Calling a method on the device with return_val = call(<identifer>, <key>, <args>)

The following example shows how Wandelscript can be used to interact with the I/O of the robot.

write(controller, "tool0", 1)
a = read(controller, "tool0")

In this example, the robot's I/O is used to write a value to the tool0 output and read it back.

Read

read(<device>, <key>) reads an I/O on the indicated device.

value = read(controller, "digital_out[7]")

Add a sync before read is executed to ensure all desired values are available. Read more on that here.

When you try to read a non-existing I/O a error will be thrown.

Write

write(<device>, <key>, <value>) updates a value to an I/O. The <value> will be written to the I/O <key> on the indicated <device>.

write(controller, "digital_out[7]", False)

In this case the boolean value False will be written to the I/O digital_out[7] on the device controller.

When you try to write a incompatible data type to an I/O a error will be thrown.

Call

call calls an external function or service which takes 0 or more arguments and can return anything available from the external service.

call(liftkit, "2:EwellixLiftkit0S/Move(newPosition)", 42.0)
  • Call a function that returns nothing with call(service, <method_name>, <params...>)
  • Call a function that returns parameters with return_value = call(service, "MethodName", params...)

In the following example an OPC UA (opens in a new tab) device is used to manipulate and access values of an external axis.

# Used to let the OPC UA connect to the liftkit axis
call(liftkit, "2:String:EwellixLiftkit0S/2:String:Connect(host)", "host")
 
# Read the value pos from the Position node
pos = read(liftkit, "2:String:EwellixLiftkit0S.Position")
 
# Call the axis to move to a new position of 42mm
call(liftkit, "2:String:EwellixLiftkit0S/2:String:Move(newPosition)", 42.0)
 
# Read the updated position from the Position node
updated_pos = read(liftkit, "2:String:EwellixLiftkit0S.Position")

Add a sync before call is executed to ensure all desired values are available.