Menlo

Connection Modes

UDP, Hybrid and LiveKit: what each carries, what it needs, and how the SDK picks one.

The SDK reaches the robot in one of three connection modes. The Robot object and every verb are the same in all three; the mode decides what the connection carries and where it works from.

ModeControl and stateCamera and audioWorks from
udpUDP on the robot's networknonethe robot's network
hybridUDP on the robot's networka LiveKit roomthe robot's network
livekita LiveKit rooma LiveKit roomwherever Asimov Manager is reachable

A saved robot carries a mode, so Robot().connect() needs no argument. Pass one to override it for a session: connect("udp").

In Code

connect.py builds the connection in code. Set MODE at the top of the file to "udp", "hybrid" or "livekit"; the three ConnectionConfig shapes sit side by side:

examples/connect.pyView on GitHub ↗
# udp: control and state over UDP on the robot's network. No camera or audio.
udp = ConnectionConfig(udp=UdpConfig(host=ROBOT_ADDRESS))

# hybrid: control and state over UDP, camera and audio over LiveKit.
hybrid = ConnectionConfig(
    udp=UdpConfig(host=ROBOT_ADDRESS),
    livekit=ManagerConfig(url=MANAGER_URL, credential=CREDENTIAL),
)

# livekit: everything through the robot's LiveKit room, wherever Asimov Manager is reachable.
livekit = ConnectionConfig(livekit=ManagerConfig(url=MANAGER_URL, credential=CREDENTIAL))

config = {"udp": udp, "hybrid": hybrid, "livekit": livekit}[MODE]

with Robot(config).connect(MODE) as robot:
    state = robot.get_state()
    print(f"connected over {MODE} to {robot.info.endpoint}")
    print(f"robot mode {state.mode.name}, armed {robot.armed}")
    if state.battery is not None:
        print(f"battery {state.battery.soc_percent:.0f} %")
    else:
        print("battery not reported")
    capabilities = ("drive", "state", "battery", "camera", "microphone", "speaker")
    print("capabilities:", ", ".join(c for c in capabilities if robot.has(c)))

The other example scripts call Robot() with no config and read the environment or a saved robot instead; see How the SDK Picks a Connection.

UDP

Commands go to the robot on UDP port 8850 and state comes back on port 8851. There is no media and no sign-in: anyone on the robot's network who can reach port 8850 can command it, so trust the network before you turn it on.

The robot sends UDP state to one host. In Asimov Manager, set the Asimov Edge parameters udp-control to on and udp-state-host to your computer's address (Asimov Manager).

One client per state port: a second script on the same computer binds UdpConfig(state_bind=("0.0.0.0", <port>)) and the robot's udp-state-port matches it.

Hybrid

Control and state travel as in udp, so a velocity command does not cross the internet. The camera, microphone and speaker travel over the robot's LiveKit room, which the SDK joins with an SDK credential from Asimov Manager. Use hybrid on the robot's network when a script needs the camera or sound.

It needs the robot's address, its Asimov Manager URL and an SDK credential. Issue the credential on the Developer page of Asimov Manager (Get an SDK Credential). The credential's role applies to the LiveKit room: a Control credential can talk through the speaker, an Observe credential watches and listens only. Commands in hybrid travel over UDP, which has no sign-in, so the role does not limit driving.

LiveKit

Everything, commands included, goes through the robot's LiveKit room. Commands travel on the data topic commands and state on the data track state, at about 10 Hz. Use livekit away from the robot's network, or when an agent framework joins the same room and the SDK is one more participant in it.

It needs the Asimov Manager URL and an SDK credential (Get an SDK Credential). The SDK asks Asimov Manager for a room token, joins, and reports robot.info.endpoint as room@url as <identity>. The credential's role applies: a Control credential drives, an Observe credential watches and listens, and the robot drops its commands.

The Asimov Manager URL is the one you open in the browser: http://192.168.22.32, http://asimov.local:8080 or a bare host name. The LiveKit URL Asimov Manager hands out is the one the robot itself uses; when it says localhost, the SDK substitutes the manager's host. A manager that redirects is reported as a ConnectError, not followed.

Every SDK session joins the room as its own participant, sdk-<credential id>-<host>-<6 hex>. ManagerConfig(label="...") fixes the suffix so a restarted script replaces its previous session instead of joining beside it. To join with a token you minted yourself, pass LiveKitConfig(url, room, token) instead of ManagerConfig; one token is one participant. To use the room without the SDK at all, see Without the SDK.

How the SDK Picks a Connection

Robot() with no argument builds its connection from the environment or a saved robot, in this order:

  1. Environment variables: MENLO_UDP_HOST, or MENLO_MANAGER_URL and MENLO_CREDENTIAL, or both pairs. They are not merged with a saved robot.
  2. A saved robot from ~/.menlo/robots.toml: the one named by MENLO_ROBOT, else the file's default, else the only one.
  3. Otherwise ConnectError, naming menlo setup and the variables.

MENLO_MODE sets the mode connect() uses with no argument and MENLO_LIMITS the velocity limits, whichever source supplied the connection. MENLO_HOME moves the store directory.

A saved robot holds everything one mode needs, and can hold more than one mode's fields:

~/.menlo/robots.toml
default = "lab"

[robots.lab]
mode = "hybrid"
udp_host = "192.168.22.32"
manager_url = "http://192.168.22.32"
credential = "..."
room = "robot-menlo-0001"

[robots.lab.limits]
vx = 0.3

udp needs udp_host; hybrid needs udp_host, manager_url and credential; livekit needs manager_url and credential. A mode whose field is missing raises ConnectError naming the field and the menlo robots add command that fills it. Save and change robots with the command line; the file is created with mode 0600 because it holds SDK credentials.

What Connect Waits For

connect() returns once the robot has reported state, so robot.get_state() is valid on the next line. It raises ConnectError after timeout (5 s by default) without state. On hybrid and livekit the media tracks are awaited for media_timeout (3 s); the capabilities the SDK claims are the tracks that arrived.

connect(require_state=False) opens the room without waiting for the firmware: camera, microphone and speaker work at once, and state and the motion verbs unlock when the first state frame arrives.

A protocol version other than the one the SDK speaks raises ProtocolMismatchError. allow_version_skew=True connects anyway, and the robot may misread the commands the SDK sends. Update the SDK or the robot instead.

A lost link ends a held velocity

After 2 s without state the SDK raises LinkLostError, sends zero velocity if it holds one, and every verb raises until you close() and connect() again. On udp and hybrid, the robot also zeroes a velocity 2 s after the last command it received. On livekit, Asimov Edge stops a held velocity when the SDK sends zero or leaves the room; see Watchdogs.

  • Quickstart: point the SDK at the robot and run the first scripts
  • Command Line: menlo setup and menlo robots
  • Drive from Python: the SDK credential and the UDP settings, step by step
  • Asimov Manager: the robot's settings
  • Reference: ConnectionConfig, UdpConfig, ManagerConfig, LiveKitConfig

How is this guide?

On this page