Menlo
Python SDKGuides

Move the Robot

Walk, strafe and turn with one verb: set_velocity.

One verb drives the robot: set_velocity. Forward, sideways and turning are three arguments of it, not three commands.

from menlo.asimov import Mode, Robot

with Robot().connect() as robot:
    if robot.state.mode is Mode.DAMP:
        robot.stand()
        robot.wait_for(Mode.STAND, timeout=20.0)

    robot.set_velocity(vx=0.3, duration=2.0, wait=True)    # forward, 2 s
    robot.set_velocity(vx=-0.3, duration=2.0, wait=True)   # backward
    robot.set_velocity(vyaw=0.8, duration=5.0, wait=True)  # turn on the spot
    robot.stop()                                           # zero: stand still, still balancing

Wake It First

A robot at rest sits in DAMP, and velocity alone will not wake it — the command is dropped before it reaches the firmware. Call stand() first, every time. See the ladder.

Holding, and Letting Go

set_velocity is held: the SDK re-sends it at 10 Hz so a dropped packet doesn't stop the robot mid-stride, and so your script can spend most of a second on vision between calls. It stops when you supersede it, when duration expires, or when the link dies.

set_velocity returns at once, duration or not. The hold runs in the background; the next verb, or the end of the with block, cuts it short. Either sleep for the duration or pass wait=True to block until the hold ended and its zero went out.

You wantDo this
walk until I say otherwiseset_velocity(vx=0.3)
walk for exactly 2 s, then continue the scriptset_velocity(vx=0.3, duration=2.0, wait=True)
walk for 2 s while doing something elseset_velocity(vx=0.3, duration=2.0) then your work
stop, keep balancingstop()
stop, go limpdamp()

Zero velocity is how it stands still

stop() sends zero on every axis. The robot stays in MOVE and the walking policy keeps balancing it — that is what standing still looks like for a free biped.

Ending a Walk

End a walk at zero velocity. Do not call stand() to finish — it stiffens rather than balances, and tips a free-standing robot over. See Safety.

robot.set_velocity(vx=0.3, duration=4.0, wait=True)
robot.stop()                  # correct ending
# robot.stand()               # tips a free-standing robot over

Turning by Heading

state.yaw is the IMU heading, counter-clockwise, relative to wherever the firmware booted. Only differences mean anything, and they wrap:

import math
before = robot.state.yaw
robot.set_velocity(vyaw=0.9, duration=math.pi / 0.9, wait=True)   # half a turn, open loop
after = robot.wait_until(lambda s: s.age_s < 0.2, timeout=2.0).yaw
turned = math.remainder(after - before, math.tau)                  # in (-π, π]

What Actually Went Out

Every verb returns a Sent describing the command that reached the wire, already clamped:

sent = robot.set_velocity(vx=2.0)
sent.command    # Velocity(vx=0.6, vy=0.0, vyaw=0.0)
sent.clamped    # True — 2.0 was above the limit

Defaults are 0.6 m/s forward, 0.6 m/s sideways, 1.5 rad/s turning. Read them from robot.info.limits, or pass your own limits= to Robot(...). The clamp is there so a typo'd vx=20 is a visibly clamped command rather than a lunge; the robot clamps again on its own side.

Next

How is this guide?

On this page