Menlo
Python SDKGuides

Read State and React

One snapshot of everything the robot reports, and how to wait on it.

robot.state is one snapshot of everything the robot last reported — already decoded. Read it as often as you like; it costs nothing, because the robot is streaming anyway.

s = robot.state

s.mode            # Mode.MOVE
s.gravity         # projected into the body frame; z ≈ -1.0 standing straight up
s.yaw             # heading, rad, counter-clockwise; None without an IMU
s.joint_pos       # one radian value per motor, firmware order
s.joint("L_Knee").pos
s.battery         # None when the robot reports no BMS
s.alerts          # severity 0 is CRITICAL
s.age_s           # how old this sample is, in seconds

Don't Sleep and Hope

Blocking on the robot's own report is better than guessing how long something takes:

robot.stand()
robot.wait_for(Mode.STAND, timeout=20.0)

robot.wait_until(lambda s: s.mode is Mode.MOVE, timeout=10.0, stale_after=0.5)

A quiet link raises StateStaleError rather than a plain WaitTimeoutError. The difference matters: "the robot never got there" and "the robot stopped talking" need different fixes. A fault is checked before your predicate, so a fall is never read as success.

Is It Still Upright?

state.upright is a convenience over the measured gravity vector, true when gravity[2] < -0.8 — about 37° of tilt. It is None, not False, when the robot did not report gravity at all: unknown and fallen are different answers.

That threshold is not the robot's

The firmware's own fall detector latches at gravity[2] > -0.5, about 60°. So upright can read False on a robot the firmware still considers fine. If the distinction matters, read state.gravity[2] and pick your own angle.

Faults

s.faulted       # an error flag, or any critical alert

Alerts are sent every 20th frame and carried forward for a short window, so a read between two alert frames still sees them. The firmware's own fault latch does not clear on its own: after a fall it keeps the robot DAMPed until it restarts, and stand() is refused until then. See Troubleshooting.

What the SDK Knows About the Body

info = robot.info
info.dof, info.joint_names        # 25 and the firmware's motor order on Asimov 1
info.capabilities                 # frozenset({'drive', 'state', 'camera', ...})
info.protocol_version, info.limits, info.endpoint

Fields the robot does not report are None, never a guessed zero. Joint names come from a table in the SDK keyed by the reported joint count; state.joint(name) raises KeyError on a body the SDK has no table for.

Callbacks

For event-driven code, register a callback instead of polling. These fire on the transport thread — keep them short, and don't call back into the robot from inside one.

robot.on_state = lambda s: ...
robot.on_mode_change = lambda prev, now: print(prev, "->", now)
robot.on_alert = lambda alert, change: print(change, alert.id)    # "raised" | "cleared"
robot.on_refused = lambda refused: ...
robot.on_link_lost = lambda exc: shutdown()

Next

How is this guide?

On this page