); `, with ` (waited s)` appended when the command waited. `RobotFaultedError` is raised
by a wait that saw a latched fault after the command went out; `sent` is set. `WaitTimeoutError.sent` is the command whose effect
did not arrive. Caller
mistakes are builtins: `ValueError` (non-finite velocity, limit or duration, wrong trajectory
length, a lone kp or kd, an unknown connection mode), `KeyError` (unknown joint name),
`RuntimeError` (`open()` on an open robot). A mode the config cannot reach raises
`ConnectError`.
Threading [#threading]
`Robot` is synchronous and thread-safe. Callbacks run on the transport thread, the one that
delivers state: keep them short. `robot.damp()` from a callback, such as `on_alert` damping
on an alert, sends DAMP and returns at once. The other commands that wait for the robot, and
`wait_until()`, raise `RuntimeError` there with nothing sent; pass `wait=False`, or call them
from your own thread.
Per-Robot Tables [#per-robot-tables]
`menlo.asimov.robots.ASIMOV_1_BIPED_JOINTS` is the 25-actuator order of the Asimov 1 biped
firmware profile, chosen by the reported joint count. `menlo.asimov.robots.PROTOCOL_VERSION`
is the protocol version the SDK was built against; `connect()` compares it with what the robot
echoes and raises `ProtocolMismatchError` on a difference.
The Protocol [#the-protocol]
Commands on udp go to port 8850 and state arrives on 8851, each datagram one bare
`asimov.io.RobotCommand` or `RobotState`. On livekit, commands are data messages on the
`commands` topic and state is the `state` data track. Every command carries
`protocol_version = 1`, a sequence number and `timestamp_us`. UDP commands are not
authenticated; the SDK credential and its role apply to the LiveKit room: every command on
livekit, and the camera and audio on hybrid. [Without the SDK](/sdk/without-the-sdk)
and the `livekit_raw/` examples speak it with the `livekit` and `asimov-protocol` packages
alone.
# Safety
The SDK is a client of the robot. It sends what you ask, bounded by your limits, and reads
back what the robot reports. Safety is the firmware's job, command handling is the robot's,
and a guard is your script's: the SDK does not refuse a command because of what the robot
reports. When in doubt, open Asimov Manager and press the E-Stop.
Before You Run a Script [#before-you-run-a-script]
. **Support the robot**, as [Supporting the Robot](/asimov/1/operate/safety#supporting-the-robot)
describes: hanging from its gantry hook with both feet on the floor for a first stand and
walk, hanging from its gantry hook or seated on a stool or bench for joint control, for
STAND from MOVE and for DAMP. A walking robot needs 2 m of clear floor ahead of it.
. **Clear the area.** Keep people, cables and furniture out of the robot's reach and keep the
robot in sight.
. **Have a way to stop the robot open.** Keep Asimov Manager open with its **E-Stop** in the
page header or the phone top bar (it needs no drive session), and the battery unit within
reach to cut power. [Stopping the Robot](/asimov/1/operate/safety/stopping) explains each.
. **Make sure nothing else is driving.** Close any Cockpit session and unpair the gamepad. Both
outrank a script, and a paired gamepad holds control even while idle.
. **Check the robot.** `python check.py` or `menlo status` reports battery, actuator
temperatures, faults, alerts and whether the state stream is fresh. Neither sends
anything. Decide for yourself whether to go ahead, or let [your own guard](#write-your-own-guard)
decide.
. **Start slow.** Pass `Robot(limits=Limits(vx=0.2, vy=0.2, vyaw=0.4))` or save limits on the
robot, and use short `duration` values until the script behaves.
`balance()`, `damp()` and `close()` are commands sent over a network link. None of them cuts
power, and none reaches a robot the link has lost. Keep the **E-Stop** open and the battery
unit within reach whenever the robot stands.
Standing and Balancing [#standing-and-balancing]
The robot has three robot modes, and only one of them keeps it upright on its own. In
**DAMP** no actuator holds a position. In **STAND** the actuators hold a standing pose and
nothing balances it: a free-standing robot in STAND tips over. In **MOVE** the walking
policy balances the robot, at zero velocity or any other. So the robot must be supported in
DAMP and STAND, and stands free only once `balance()` has returned and it reports MOVE.
This is the sequence for a script, and the order in which the [Quickstart](/sdk/quickstart)
runs it as separate files:
```python
import sys
from menlo.asimov import NotReadyError, Robot, RobotFaultedError, WaitTimeoutError
with Robot().connect() as robot:
# 1. The robot hangs from its gantry hook with both feet on the floor.
try:
robot.stand() # DAMP -> STAND; returns once the robot is armed
robot.balance() # armed STAND -> MOVE at zero velocity; returns once MOVE is reported
except RobotFaultedError as exc: # latched until the firmware restarts
sys.exit(f"faulted: {exc}")
except NotReadyError as exc: # no live state: nothing was sent
sys.exit(str(exc))
except WaitTimeoutError as exc: # sent, but the robot did not arm or reach MOVE in time
sys.exit(str(exc))
# 2. Only now: slacken the gantry until the robot carries its own weight.
input("Robot balancing in MOVE. Slacken the gantry, then press Enter to walk. ")
# 3. Walk. Every set_velocity() ends with zero velocity; the robot stays in MOVE.
robot.set_velocity(vx=0.2, duration=3.0)
robot.balance() # zero velocity: balancing in place
# 4. Support the robot again before it leaves MOVE.
input("Take up the gantry's slack so it holds the robot, then press Enter to damp. ")
robot.damp() # every actuator stops holding its position
```
Why each step is where it is:
. **`stand()` on the gantry hook, both feet on the floor.** STAND holds a pose and does not
balance, so the hook carries the robot. `stand()` sends STAND and returns once the robot
reports STAND and has been upright for 0.5 s: the firmware enters MOVE only after that, so
`stand()` returning means the robot is armed. It raises `NotReadyError` with nothing sent
only when there is no live state.
. **`balance()` before the support comes off.** From an armed STAND, `balance()` sends zero
velocity, which is what puts the robot in MOVE, and returns once the robot reports MOVE.
It does not wait for arming: after `stand()` the robot is already armed. MOVE at zero
velocity is the only state in which the robot stands free, so the hook keeps carrying the
robot until `balance()` has returned: until then the robot is in STAND, and a robot in
STAND that loses its support tips over.
. **Slacken the gantry only after `balance()` returns.** The SDK cannot see the gantry; the
script asks you. Slacken it gradually until the robot carries its own weight, with 2 m of
clear floor ahead.
. **Walk, then `balance()`.** `set_velocity()` holds the velocity for its `duration` and
ends with zero velocity. `balance()` in MOVE is the same zero velocity, sent at once: it
ends a walk and the robot balances in place.
. **Support the robot before `damp()`.** `damp()` is sent at once, and a standing robot
falls when its actuators let go. Take up the gantry's slack, or seat the robot on a stool
or bench, first. A script cannot see the support, so this one asks before it damps; to go
through STAND first, as the robot's own controls do
([Bring It Back Down](/asimov/1/operate/drive/stand-up#bring-it-back-down)), see
[Bringing the Robot to Rest](#bringing-the-robot-to-rest).
Catch all three errors around `stand()` and `balance()`. A `RobotFaultedError` means the
command was sent and a critical alert, a fall included, latched DAMP until the firmware
restarts; a `NotReadyError` means there was no live state and nothing was sent; a
`WaitTimeoutError` means the command was sent and the robot did not report the mode in
time, so read `robot.get_state().mode` before you decide what to do next. Keep Asimov Manager
open at the **E-Stop** throughout: nothing in the SDK is an emergency stop.
Bringing the Robot to Rest [#bringing-the-robot-to-rest]
The SDK sends every mode change you ask for, in any robot mode, and the firmware decides what
it does. Two of them need the robot supported first, because the SDK cannot see the support:
* **MOVE to STAND.** STAND has no balance loop: it stiffens the joints into a pose, and a
free-standing robot asked to stand from MOVE tips over. `stand()` from MOVE is sent as
asked, so hang the robot from its gantry hook or seat it on a stool or bench first.
* **Anything to DAMP.** DAMP makes every actuator compliant, so a standing robot falls. Support
the robot the same way before `damp()`.
`rest.py` goes from MOVE to STAND to DAMP, the order the robot's own controls use. It asks
first whether the robot is on its gantry hook or seated on a stool or bench, unless you set
`YES = True`; sends STAND and waits until the robot reports it; then sends DAMP and waits
until the robot reports that, printing each robot mode. A robot already in DAMP is left as it
is:
```python lineNumbers=22
mode = robot.get_state().mode
print(f"robot mode {mode.name}")
if mode in (Mode.DAMP, Mode.FAULT_DAMP):
print("already at rest; nothing sent")
sys.exit(0)
if not YES:
question = "Is the robot on its gantry hook or seated on a stool or bench? [y/N] "
if input(question).strip().lower() not in ("y", "yes"):
print("nothing sent")
sys.exit(0)
try:
# STAND first: the robot holds its pose, so DAMP does not drop it from a walk.
robot.stand(wait=False)
robot.wait_until(lambda s: s.mode is Mode.STAND, timeout=STAND_TIMEOUT_S)
print(f"robot mode {robot.get_state().mode.name}")
robot.damp() # returns once the robot reports DAMP
except (NotReadyError, WaitTimeoutError) as exc: # no live state, a fault, or not reported
print(exc)
sys.exit(1)
print(f"robot mode {robot.get_state().mode.name}")
```
`rest.py` is not an emergency stop: for that, use the E-Stop in Asimov Manager, or cut power
at the battery unit.
If Something Goes Wrong [#if-something-goes-wrong]
. **Press the E-Stop in Asimov Manager, or cut power at the battery unit.** The E-Stop damps
every actuator at once and a free-standing robot falls; use it when a robot lying on the
floor is better than what is happening now. If the robot's software is not running, the
E-Stop is disabled: cut power at the battery unit. Do not try to catch a falling robot.
. **Stop the script.** Ctrl-C in a script that uses `with Robot().connect()` leaves the
block, and `close()` sends zero velocity. A script that keeps running keeps re-sending
its velocity at 10 Hz; `menlo balance` from another terminal does not end it.
. **Read what the robot reports, and write it down.** `menlo status` shows the robot mode, a
**FAULTED** badge and what latched, from the current alerts and the latched `error_flags`.
In a script, `state.faulted` and the `faulted` line from `preflight()` name the causes,
and the waits of `stand()`, `balance()`, `set_velocity()`, `set_joints()` and
`wait_until()` raise `RobotFaultedError`.
. **Recover the robot.** Follow
[Recovering After an E-Stop or a Fall](/asimov/1/operate/safety/stopping#recovering-after-an-e-stop-or-a-fall):
support and inspect the robot. Its **Turn Off Robot** step restarts the firmware, so do
step 3 first.
. **Restart the firmware, if the recovery did not.** A critical alert holds the robot in
DAMP, and `error_flags` set, until the firmware restarts; restarting clears the record of
what latched. Use **Restart** on the **Firmware (RPU)** row of the
[Troubleshoot page](/asimov/1/operate/troubleshooting), or **Turn Off Robot** and
**Start Robot** on Overview.
Do not call `stand()` on a free-standing robot that is walking or falling. STAND has no
balance loop: it stiffens the joints into a pose, and a free-standing robot in that pose tips
over. End a walk with `balance()`, which leaves the robot in MOVE, balancing in place.
Who Stops What [#who-stops-what]
| Layer | Stops | Does not stop |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| **SDK** | a held velocity: zero on `balance()`, `close()`, the end of a `with` block, a lost link | anything after a fault; another program's velocity |
| **The robot** | velocity and trajectory in DAMP (dropped); a velocity 2 s after the last command on `udp` and `hybrid`; a trajectory 2 s after the last setpoint (DAMP) | a velocity a script keeps re-sending |
| **Firmware** | MOVE before an armed STAND (the robot stays in STAND, nothing reports it); everything, on a critical alert (DAMP, latched) | a command because of where it came from: it obeys whichever arrived last; STAND from MOVE on a free-standing robot |
The SDK refuses a command only when there is no live state. It also clamps every velocity
to your `Limits`, runs every wait against fresh state,
and fences a running `set_joints()` so that any verb ends it. `close()` sends zero velocity
only when this `Robot` holds one; a `Robot` that sent nothing does not stop another program.
After `close()` stops re-sending a trajectory, the robot enters DAMP 2 s later. Neither the
robot nor the firmware checks command timestamps.
Who Holds Control [#who-holds-control]
The robot gives the Cockpit priority over a paired gamepad, the gamepad over `udp`, and
`udp` over `livekit`. The state stream does not say who holds control, so a walk that does
not happen, with no fault and no refusal, is the first thing to check.
A gamepad paired to the robot holds control while nobody touches it. Every velocity a script
sends is dropped without a report. Unpair the gamepad on the **Controller** page of Asimov
Manager before you run a script; see [Gamepad](/asimov/1/operate/drive/gamepad#who-drives).
Limits [#limits]
The firmware caps `vx` and `vy` at 0.4 m/s and `vyaw` at 0.8 rad/s. `Limits()` defaults to
those caps and the SDK clamps every velocity to them before it leaves, so `Sent.clamped` is
`True` exactly when the robot would not have walked at the speed you asked for. A tighter
envelope is `Robot(limits=Limits(vx=0.2, vy=0.2, vyaw=0.4))`, `MENLO_LIMITS="0.2,0.2,0.4"`,
or `limits` on a saved robot. Limits above the caps are sent as asked and the firmware clamps
them.
Arming [#arming]
MOVE is entered only from STAND held upright, gravity z below -0.87 (under 30 degrees of
tilt), for 0.5 s. A velocity sent earlier is not refused and not reported: the robot stays in
STAND. `stand()` returns once the robot is armed, so `stand()` then `balance()` enters MOVE,
and `robot.armed` reads the same test the firmware applies. `balance()` and `set_velocity()`
send without waiting for arming: a velocity that reaches an armed STAND puts the robot in
MOVE, and `balance()` that reaches a STAND that is not armed times out saying so. The SDK counts
the 0.5 s from its own samples, so a new session sees an armed robot as armed 0.5 s after it
connects. A robot that reports no gravity leaves `robot.armed` as `None`.
Faults [#faults]
A critical alert, such as a fall, actuator over-temperature, battery protection, a joint past
its limit, lost actuator communication or a watchdog, makes the firmware latch DAMP. The
latch, and `error_flags`, stay until the firmware restarts. The SDK reports it as
`State.faulted` and as `faulted` from `preflight()`, and drops a held velocity without
sending anything more. A command is still sent; the wait that sees the fault, in `stand()`,
`balance()`, `set_velocity()`, `set_joints()` or `wait_until()`, raises `RobotFaultedError`
with `.sent` set. `damp()` returns, since the robot is already in DAMP.
Watchdogs [#watchdogs]
| Watchdog | Where | What happens |
| -------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Velocity** | the robot, `udp` and `hybrid` | zero velocity 2 s after the last velocity command; the robot stays in MOVE, balancing |
| **Trajectory** | the robot, every mode | DAMP 2 s after the last setpoint |
| **Link** | SDK, every mode | no state for 2 s: zero velocity if one is held, `on_link_lost` fires, every verb raises `LinkLostError` until `close()` and `connect()` |
A script that holds a velocity re-sends it at 10 Hz, so the velocity watchdog does not stop
it: end it with `balance()`, a `duration`, or `close()`.
On a `livekit` connection, Asimov Edge stops a held velocity when the SDK sends zero or
leaves the room. A script that hangs while it holds a velocity keeps the robot walking until
LiveKit drops the participant. Bound every hold with a `duration`, and prefer `udp` or
`hybrid` for a control loop.
What the SDK Reports [#what-the-sdk-reports]
The SDK does not refuse a command because of what the robot reports. Its robot mode, a
latched fault, an alert, an actuator temperature or the battery never stop `stand()`,
`balance()`, `set_velocity()`, `trajectory()` or `set_joints()`, and the firmware decides
what a command does. The firmware warns at 60 °C and latches DAMP at 80 °C on an actuator,
and warns below 20 % battery.
The one refusal is no live state. `stand()`, `balance()` outside MOVE, `set_velocity()`,
`trajectory()` and `set_joints()` read the latest state sample before they send; on a live
stream that adds no delay. Without one (`no_state`, `stale_state`) they wait up to the
command's `timeout`, then raise `NotReadyError` with nothing sent. After a STAND sent moments
ago whose report has not arrived, they wait up to 1 s for that report, then send anyway. A
closed `Robot` raises `NotConnectedError`, a lost link `LinkLostError` and a protocol version
mismatch `ProtocolMismatchError`, at once. `trajectory()` and
`set_velocity(hold=False)` check once and do not wait unless given a `timeout`. `balance()`
in MOVE and `damp()` are sent at once.
```text
not ready to move: the latest state is 1.2 s old (limit 0.5 s) (stale_state); check the network link to the robot (waited 5.0 s)
```
Waits stay truthful. `stand()` returns at STAND and armed, `balance()` at MOVE, `damp()` at
DAMP. A latched fault seen during a wait raises `RobotFaultedError`, after the command was
sent, and a robot that does not get there raises `WaitTimeoutError` saying what it reports:
```python
from menlo.asimov import NotReadyError, Robot, RobotFaultedError, WaitTimeoutError
with Robot().connect() as robot:
try:
robot.stand()
robot.balance()
robot.set_velocity(vx=0.3, duration=3.0)
except RobotFaultedError as exc: # sent; latched until the firmware restarts
print("faulted:", exc)
except NotReadyError as exc: # no live state: nothing was sent
print(exc)
except WaitTimeoutError as exc: # sent, but the robot did not arm or reach MOVE in time
print(exc)
```
`RobotFaultedError` is a `NotReadyError`, so catch it first to tell a latched fault apart.
`robot.preflight(action)` lists the facts for `"stand"`, `"move"` (`balance()` and
`set_velocity()`) or `"trajectory"` (`trajectory()` and `set_joints()`). It sends nothing,
never waits and never raises for a robot condition. `check.ok` is `True` when nothing is
blocking, which means there is live state; `check.problems` lists the problems, blocking
first. Only the blocking codes stop a command; the others are information, and nothing in the
SDK acts on them. `check.py` prints the raw facts and `preflight()` for all three actions:
```python lineNumbers=16
# The SDK counts the 0.5 s a robot in STAND must be upright to arm from its own samples.
time.sleep(0.6)
s = robot.get_state() # one sample: every fact below comes from it
print(f"robot mode {s.mode.name}, armed {robot.armed}, faulted {s.faulted}")
print(f"alerts {', '.join(a.name for a in s.alerts) or 'none'}")
temps = [j.temp for j in s.joints if j.temp is not None]
print(f"hottest joint {max(temps):.0f} C" if temps else "joint temperatures not reported")
print(f"battery {s.battery.soc_percent:.0f} %" if s.battery else "battery not reported")
print(f"state {s.age_s:.2f} s old")
for action in ("stand", "move", "trajectory"):
print(robot.preflight(action)) # "ready to move" means live state, and the facts
```
| Code | Blocks | Meaning | What clears it |
| --------------- | ------ | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `not_connected` | yes | closed, link lost, or protocol mismatch | `close()` and `connect()` |
| `no_state` | yes | the session is open and the robot has not reported state | wait for the firmware; see [No State on udp or hybrid](/sdk/troubleshooting#no-state-on-udp-or-hybrid) |
| `stale_state` | yes | latest state older than 0.5 s | a command waits for the next sample; otherwise check the link |
| `faulted` | no | a critical alert is latched, or the robot reports FAULT\_DAMP; the alerts that caused it are named | restart the firmware |
| `alerts` | no | the firmware reports active alerts, named | |
| `not_armed` | no | `move` from STAND before the 0.5 s upright hold | hold STAND upright |
Write Your Own Guard [#write-your-own-guard]
A rule about what your script will not drive through is yours to write. Read
`robot.get_state()` once, decide, and only then send:
```python
s = robot.get_state() # one sample; every fact from it
if s.faulted or any(j.temp is not None and j.temp >= 60 for j in s.joints):
raise SystemExit(f"not driving: robot mode {s.mode.name}, faulted {s.faulted}")
```
`guard.py` is an optional guard to start from. Its limits are constants at the top of the
file: `MAX_JOINT_TEMP_C` (60 °C), `MIN_BATTERY_PERCENT` (20 %), `REFUSE_WHEN_FAULTED` and
`REFUSE_ON_ALERTS` (warning and critical alerts). The defaults stop at the firmware's own
warnings, before its latches. It prints what it finds and exits with status 1 when your rule
fails:
```python lineNumbers=24
def guard(robot: Robot) -> None:
"""Print what the robot reports, and exit with status 1 when your rule fails."""
s = robot.get_state() # one sample: every fact below comes from it
stop: list[str] = []
print(f"guard: robot mode {s.mode.name}, armed {robot.armed}, faulted {s.faulted}")
if s.faulted and REFUSE_WHEN_FAULTED:
stop.append("the firmware latched DAMP; it stays so until the firmware restarts")
temps = [(j.temp, j.name or f"joint {i}") for i, j in enumerate(s.joints) if j.temp is not None]
if temps:
hottest, joint = max(temps)
print(f"guard: hottest joint {joint} {hottest:.0f} C")
if hottest >= MAX_JOINT_TEMP_C:
stop.append(f"{joint} is at {hottest:.0f} C (your limit {MAX_JOINT_TEMP_C:.0f} C)")
else:
print("guard: joint temperatures not reported")
if s.battery is not None:
print(f"guard: battery {s.battery.soc_percent:.0f} %")
if s.battery.soc_percent < MIN_BATTERY_PERCENT:
stop.append(
f"battery at {s.battery.soc_percent:.0f} % (your limit {MIN_BATTERY_PERCENT:.0f} %)"
)
if s.battery.protecting:
stop.append("the battery management system is protecting the pack")
else:
print("guard: battery not reported")
for alert in s.alerts:
print(f"guard: alert {alert.name} (severity {alert.severity})")
serious = sorted({a.name for a in s.alerts if a.severity <= 1}) # 0 critical, 1 warning
if serious and REFUSE_ON_ALERTS:
stop.append(f"active alerts: {', '.join(serious)}")
if stop:
print("guard: not going ahead: " + "; ".join(stop))
print("guard: this is your rule in guard.py; change it, or delete the guard(robot) line")
sys.exit(1)
```
The motion examples, `stand.py`, `balance.py`, `walk.py`, `keyboard.py`, `stream_velocity.py`,
`move_joints.py` and `wait_until.py`, call `guard(robot)` before they send anything. Change
the constants, change `guard()`, or delete the `guard(robot)` line in a script to drive
without it. Run on its own, `python guard.py` prints what it finds and sends nothing.
Shutdown [#shutdown]
Leaving the `with` block calls `close()`: zero velocity if one is held, then the link drops.
The robot stays in MOVE, balancing in place, and the Cockpit or the gamepad can take it from
there. Call `damp()` at the end of a session only on a supported robot, hanging from its
gantry hook or seated on a stool or bench: every actuator stops holding its position and a standing
robot falls.
`damp.py` asks before it sends, then waits for the robot to report DAMP:
```python lineNumbers=17
# damp() is never refused, so there is no check here, only the question.
if not YES:
mode = robot.get_state().mode.name
question = f"Robot mode {mode}. Is the robot supported? Damp now? [y/N] "
if input(question).strip().lower() not in ("y", "yes"):
print("nothing sent")
sys.exit(0)
try:
robot.damp() # returns once the robot reports DAMP
except WaitTimeoutError as exc: # sent, but DAMP was not reported: the message says what next
print(exc)
sys.exit(1)
print(f"robot mode {robot.get_state().mode.name}")
```
Turning the robot off is in [Power](/asimov/1/operate/power#turn-the-robot-off).
Related [#related]
* [Stopping the Robot](/asimov/1/operate/safety/stopping): the E-Stop, pause, damp and cutting power
* [Stand Up and Walk](/asimov/1/operate/drive/stand-up): the same sequence from the Cockpit
* [Move the Robot](/sdk/move): stand, balance and walk
* [Control Joints](/sdk/joints): joint control on a supported robot
* [Troubleshooting](/sdk/troubleshooting): each error and its fix
# Read State
`robot.get_state()` returns the latest sample the robot reported, decoded: robot mode, joints, IMU,
battery, alerts. Reading it costs nothing, because the robot streams it anyway. A field the
robot does not report is `None`, never a guessed zero.
`read_state.py` is a tour of the state: robot mode, arming, what latched, battery, the
hottest actuators, gravity, state age and rate, alerts. It sends nothing and works in every
connection mode.
```python lineNumbers=36
# robot.get_state() is the latest sample the robot sent. It is a snapshot: read it again
# for newer values. A field the robot does not report is None, never a guess.
s = robot.get_state()
# Robot mode: DAMP (no actuator holds a position), STAND (a held standing pose, no
# balance) or MOVE (the walking policy balances the robot, at zero or any velocity).
print(f"robot mode {s.mode.name}")
# Armed: the firmware accepts MOVE only after STAND has been held upright for 0.5 s.
# None means the SDK cannot tell (no gravity vector reported).
print(f"armed {robot.armed}")
# Faulted: a critical alert latched DAMP. The robot stays in DAMP, and error_flags stay
# set, until the firmware restarts. The preflight check names what latched.
print(f"faulted {s.faulted}")
for problem in robot.preflight("stand").problems:
if problem.code == "faulted":
print(f" {problem.message}")
# Battery, from the battery management system. None when the robot reports none.
if s.battery is not None:
b = s.battery
print(
f"battery {b.soc_percent:.0f} %, {b.voltage_v:.1f} V, {b.current_a:+.1f} A, "
f"warmest cell {b.max_cell_temp_c:.0f} °C, protecting {b.protecting}"
)
else:
print("battery not reported")
# Actuators: one Joint per actuator, with the firmware's name, position (rad), velocity
# (rad/s), current (A) and temperature (°C). The firmware warns at 60 °C and latches
# DAMP at 80 °C.
temps = sorted(((j.temp, j.name) for j in s.joints if j.temp is not None), reverse=True)
if temps:
hottest = ", ".join(f"{name} {temp:.0f} °C" for temp, name in temps[:HOTTEST])
print(f"hottest {hottest}")
else:
print("hottest actuator temperatures not reported")
# IMU: gravity as the body sees it. z is close to -1 when the robot is upright;
# upright is True below -0.8. euler is (roll, pitch, yaw) in rad from the IMU quaternion.
if s.gravity is not None:
gx, gy, gz = s.gravity
print(f"gravity ({gx:+.2f}, {gy:+.2f}, {gz:+.2f}), upright {s.upright}")
if s.euler is not None:
roll, pitch, yaw = s.euler
print(f"orientation roll {roll:+.2f}, pitch {pitch:+.2f}, yaw {yaw:+.2f} rad")
# Alerts the firmware reports now. Severity 0 is critical: it latches DAMP.
alerts = [f"{a.name}{' (critical)' if a.critical else ''}" for a in s.alerts]
print(f"alerts {', '.join(alerts) or 'none'}")
# Freshness: how old the sample is. Decisions to move are made on samples at most
# 0.5 s old; robot.preflight() reports stale_state beyond that.
print(f"state age {s.age_s * 1000:.0f} ms")
# The same fields as a stream, a few times a second.
for _ in range(STREAM_LINES):
time.sleep(STREAM_PERIOD_S)
s = robot.get_state()
now = time.monotonic()
rate = sum(1 for t in tuple(arrivals) if now - t <= 1.0)
battery = f"{s.battery.soc_percent:.0f} %" if s.battery else "n/a"
print(
f"{s.mode.name:5} armed {robot.armed} upright {s.upright} faulted {s.faulted} "
f"battery {battery} age {s.age_s * 1000:.0f} ms rate {rate:.0f} Hz"
)
```
| Field | Meaning |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| `state.mode` | `Mode.DAMP`, `Mode.STAND`, `Mode.MOVE`, `Mode.FAULT_DAMP` or `Mode.UNKNOWN` |
| `state.joints` | one `Joint(name, pos, vel, current, temp)` per actuator, firmware order |
| `state.joint("L_Knee")` | a joint by name; `KeyError` on an unknown name |
| `state.joint_pos` | one radian value per actuator |
| `state.gravity` | gravity projected into the body frame; z near -1 when upright |
| `state.gyro`, `state.quat`, `state.euler`, `state.yaw` | angular rate, orientation, and heading in radians counter-clockwise |
| `state.battery` | `Battery(voltage_v, current_a, soc_percent, max_cell_temp_c, protection)` or `None` |
| `state.alerts`, `state.error_flags`, `state.faulted` | alerts and faults reported by the firmware |
| `state.age_s` | seconds since this sample arrived |
Check the Facts [#check-the-facts]
`robot.preflight(action)` reads the latest state and lists the facts for `"stand"`,
`"move"` or `"trajectory"`: whether there is live state, a latched fault, active alerts, and
in STAND whether the robot is armed. It sends nothing, never waits and never raises for a
robot condition. `check.py` prints the raw facts and all three:
```python lineNumbers=16
# The SDK counts the 0.5 s a robot in STAND must be upright to arm from its own samples.
time.sleep(0.6)
s = robot.get_state() # one sample: every fact below comes from it
print(f"robot mode {s.mode.name}, armed {robot.armed}, faulted {s.faulted}")
print(f"alerts {', '.join(a.name for a in s.alerts) or 'none'}")
temps = [j.temp for j in s.joints if j.temp is not None]
print(f"hottest joint {max(temps):.0f} C" if temps else "joint temperatures not reported")
print(f"battery {s.battery.soc_percent:.0f} %" if s.battery else "battery not reported")
print(f"state {s.age_s:.2f} s old")
for action in ("stand", "move", "trajectory"):
print(robot.preflight(action)) # "ready to move" means live state, and the facts
```
`check.ok` is `True` when nothing is blocking, which means there is live state;
`check.problems` lists the problems, blocking first, then information; `check.has(code)`
tests one; `str(check)` is readable. Only `not_connected`, `no_state` and `stale_state`
block; `faulted`, `alerts` and `not_armed` are information. Each code is in
[What the SDK Reports](/sdk/safety#what-the-sdk-reports).
`stand()`, `balance()` outside MOVE, `set_velocity()`, `set_joints()` and `trajectory()` wait
for live state before they send, up to `timeout` seconds, then raise `NotReadyError`,
carrying `.preflight` and `.problems`, with nothing sent. Nothing else stops them: the robot
mode, a fault, an alert, a hot actuator or a low battery is for your script to decide on,
from `robot.get_state()`, as `guard.py` does; see
[Write Your Own Guard](/sdk/safety#write-your-own-guard).
`robot.armed` is the arming test on its own: `True` in MOVE, or in STAND once the robot has
been upright for 0.5 s; `False` in DAMP or before that; `None` without state, closed, in
UNKNOWN, or in STAND with no gravity reported.
Wait on the Robot [#wait-on-the-robot]
Waits read the robot's own report, never a sleep. The code below moves the robot, so it
belongs after the [checklist](/sdk/safety#before-you-run-a-script) and
[Standing and Balancing](/sdk/safety#standing-and-balancing):
```python
if robot.get_state().mode is Mode.DAMP:
robot.stand() # returns once the robot reports STAND and is armed
robot.balance() # returns once the robot reports MOVE
robot.set_velocity(vx=0.2, duration=2.0) # checks the robot, then sends
robot.wait_until(lambda s: s.mode is Mode.MOVE and s.upright, timeout=10.0)
```
`stand()`, `balance()`, `damp()`, `set_joints(wait=True)` and `wait_until` read the robot's report and raise
`WaitTimeoutError` (also a `TimeoutError`) when the condition is not met in time, with
`.last` holding the last state seen; `.sent` on a command carries what went out. A stream
that goes quiet raises `StateStaleError` instead: a robot that did not reach the state and a
stream that stopped need different fixes. A fault is checked before the predicate and raises
`RobotFaultedError`, so a fall is never read as success. `stale_after` on `wait_until`
defaults to the link timeout of 2 s.
Upright [#upright]
`state.upright` is `True` when gravity z is below -0.8, about 37 degrees of tilt, and `None`
when the robot reports no gravity: unknown and fallen are different answers. It is not the
arming test; the firmware arms at -0.87, under 30 degrees, and `robot.armed` reads that.
Faults [#faults]
`state.faulted` is `True` when `error_flags` is set or any alert is critical (severity 0).
A critical alert, such as a fall, actuator over-temperature, battery protection, a joint past
its limit, lost actuator communication or a watchdog, makes the firmware latch DAMP. It stays
latched, and `error_flags` stay set, until the firmware restarts. The robot may report it as
`Mode.FAULT_DAMP`. Commands are still sent; the waits of `stand()`, `balance()`,
`set_velocity()`, `set_joints()` and `wait_until` raise `RobotFaultedError`; `preflight()`
reports `faulted`; nothing a script sends clears it.
`alert.name` is the firmware's name for an alert, `alert.critical` its severity test.
What the SDK Knows About the Body [#what-the-sdk-knows-about-the-body]
```python
info = robot.info
info.dof, info.joint_names # 25 and the firmware's actuator order on Asimov 1
info.capabilities # frozenset({'drive', 'state', 'battery', 'camera', ...})
info.protocol_version, info.limits, info.endpoint, info.transport
```
Joint names come from `menlo.asimov.robots.ASIMOV_1_BIPED_JOINTS`, chosen by the reported
joint count. `robot.has("camera")` and `robot.require("drive", "battery")` test the
capabilities; the set is what this connection carries, so it holds no media on udp.
Callbacks [#callbacks]
For event-driven code, register a callback instead of polling. Callbacks run on the
transport thread, the one that delivers state: keep them short. `robot.damp()` from a
callback, such as `on_alert` damping on an alert, sends DAMP and returns at once. The other
commands that wait for the robot, and `wait_until()`, raise `RuntimeError` there with nothing
sent; pass `wait=False`, or call them from your own thread.
| Callback | Arguments |
| ---------------------- | ---------------------------------------------------------- |
| `robot.on_state` | every accepted `State` |
| `robot.on_mode_change` | `(before, after)`, both `Mode` |
| `robot.on_alert` | `(alert, change)`, with `change` `"raised"` or `"cleared"` |
| `robot.on_link_lost` | the `LinkLostError` |
Related [#related]
* [Move the Robot](/sdk/move): what to do once the robot is ready
* [Safety](/sdk/safety#what-the-sdk-reports): every preflight code
* [SDK Reference](/sdk/reference#state): every field and type
# Troubleshooting
Start with `menlo status`: it connects, reads state for a second, and prints what the robot
reports without sending anything. The SDK refuses a command only when there is no live
state; everything else the robot reports is for your script to judge.
`ConnectError`: Nothing to Connect To [#connecterror-nothing-to-connect-to]
`Robot()` found neither `MENLO_UDP_HOST` nor `MENLO_MANAGER_URL` with `MENLO_CREDENTIAL`
in the environment, and no saved robot. Run `menlo setup`, or pass a `ConnectionConfig`.
With several robots saved and no default, `menlo robots use NAME` picks one, or set
`MENLO_ROBOT`. `MENLO_MANAGER_URL` without `MENLO_CREDENTIAL`, or the reverse, is the same
error: they go together.
`ConnectError`: The Saved Robot Lacks What the Mode Needs [#connecterror-the-saved-robot-lacks-what-the-mode-needs]
The message names the fix, such as `menlo robots add lab --udp `. `hybrid`
needs the robot's address, the Asimov Manager URL and an SDK credential; `udp` the address;
`livekit` the Manager URL and the credential. `cfg.available_modes()` says which modes a config can reach.
`ConnectError`: Asimov Manager Refused the Credential [#connecterror-asimov-manager-refused-the-credential]
The SDK credential is wrong or revoked, or the URL is not Asimov Manager. Issue a new one on
the Developer page of Asimov Manager with the Control role
([Get an SDK Credential](/asimov/1/operate/drive/python-sdk#get-an-sdk-credential)) and run
`menlo setup` again. A credential with the Observe role
connects but cannot drive on livekit. A manager that redirects is reported rather than followed: use
the address it redirected to.
`ConnectError`: No State From the Robot on UDP [#connecterror-no-state-from-the-robot-on-udp]
The robot never reported in. The robot sends UDP state to one address, so check, in order:
. `udp-control` is on in the Asimov Edge parameters in Asimov Manager.
. `udp-state-host` is this machine's address as the robot sees it, and `udp-state-port`
is the port the SDK binds (8851 by default).
. Nothing blocks port 8851 inbound: a firewall or a VPN. See
[No State on udp or hybrid](#no-state-on-udp-or-hybrid).
`menlo robots add NAME --udp HOST` runs this check and prints the same advice.
No State on udp or hybrid [#no-state-on-udp-or-hybrid]
On `udp` and `hybrid`, the robot listens for commands on UDP port 8850 and sends its state to
one address: the `udp-state-host` and `udp-state-port` (8851 by default) in the Asimov Edge
parameters. Commands go out from your computer, but state comes in to it, so a firewall on
your computer that blocks inbound UDP 8851 from the robot leaves the SDK with no state:
`connect()` raises `ConnectError`, a command raises `NotReadyError` with `no_state` or
`stale_state`, and `menlo stand`, `balance` and `walk` print `Not feasible:`. `livekit` is
not affected.
First check that `udp-state-host` is this computer's address as the robot sees it; the robot
sends state to that one host only. Then allow inbound UDP 8851 from the robot:
* **Linux** with ufw: `sudo ufw allow from to any port 8851 proto udp`. With
firewalld: `sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="" port port="8851" protocol="udp" accept'`, then
`sudo firewall-cmd --reload`.
* **macOS**: the firewall allows or blocks per application. Open **System Settings >
Network > Firewall > Options** and allow incoming connections for the Python you run the
SDK with, or answer **Allow** when macOS asks the first time a script connects.
* **Windows**: answer **Allow** for private networks when Windows asks the first time a
script connects, or in an administrator PowerShell run
`New-NetFirewallRule -DisplayName "Asimov state" -Direction Inbound -Protocol UDP -LocalPort 8851 -Action Allow`.
Run `menlo status` again: the state rate and a fresh sample show that state arrives.
`ConnectError`: Could Not Bind the State Port [#connecterror-could-not-bind-the-state-port]
Another process holds port 8851; one client per port on udp. Close it, or use
`state_bind=("0.0.0.0", )` and set `udp-state-port` to match.
`ProtocolMismatchError` [#protocolmismatcherror]
The robot speaks another protocol version than the SDK was built against. Update whichever is
behind. `connect(allow_version_skew=True)` proceeds anyway, and the robot may misread the commands the
SDK sends.
`NotReadyError` [#notreadyerror]
There was no live state to send against, and nothing was sent. `stand()`, `balance()`
outside MOVE, `set_velocity()`, `set_joints()` and `trajectory()` read the latest state
sample before they send. The codes are `no_state` (the robot has not reported) and
`stale_state` (the latest sample is older than 0.5 s); both are waited out for up to the
command's `timeout` first, and the message then ends in `(waited s)`. A closed
`Robot` raises `NotConnectedError`, a lost link `LinkLostError` and a protocol version
mismatch `ProtocolMismatchError` instead, at once.
`e.problems` carries them, `e.has(code)` tests one and `e.preflight` is the whole report.
Check the link with `menlo status`. On `udp` or `hybrid`, see
[No State on udp or hybrid](#no-state-on-udp-or-hybrid).
The robot mode, a fault, an alert, a hot actuator or a low battery never raise
`NotReadyError`: the command is sent and the firmware decides. A rule against them is yours
to write; see [Write Your Own Guard](/sdk/safety#write-your-own-guard).
`RobotFaultedError` [#robotfaultederror]
A wait found `state.faulted` set after the command was sent: a critical alert, such as a
fall, over-temperature or battery protection, has latched DAMP. The message and `state.alerts`
name it; write it down before anything restarts the firmware, since restarting clears the
record of what latched. Nothing a script sends clears it. Then follow
[Recovering After an E-Stop or a Fall](/asimov/1/operate/safety/stopping#recovering-after-an-e-stop-or-a-fall)
to support and inspect the robot; its **Turn Off Robot** step restarts the firmware. See
[If Something Goes Wrong](/sdk/safety#if-something-goes-wrong).
`RobotFaultedError` is a `NotReadyError`, so `except NotReadyError` catches it too; catch it
first to tell a fault apart. `e.sent` is the command that went out.
`WaitTimeoutError` [#waittimeouterror]
A command was sent and the robot did not report the effect in time. `e.last` is the last
state seen and `e.sent` what went out.
* `stand()`: the robot did not report STAND and arming within `timeout` (10 s). See the next
section.
* `balance()` from STAND: the robot did not report MOVE within `timeout` (5 s) after the
zero velocity went out. The message says when the robot was not armed: wait until
`robot.armed` is `True`, then `balance()` again. Otherwise another controller may hold
it: the Asimov Manager Cockpit or a paired gamepad outranks a script, and a command it
outranks has no effect.
* `balance()` from DAMP: raised at once, `the robot is in DAMP: stand() first`.
* `damp()`: the robot did not report DAMP within `timeout` (5 s). Check that state is still
arriving with `menlo status`, and that a `trajectory()` loop is not overwriting the verb.
* `set_joints(wait=True)`: a joint did not reach its target within `tolerance`. It may be
blocked, or the gains may be too soft to hold it.
* `wait_until()`: the predicate stayed false; the message names the robot mode and the age
of the last sample.
`stand()` Times Out and the Robot Stays DAMP [#stand-times-out-and-the-robot-stays-damp]
* **A fault is latched**: `stand()` raises `RobotFaultedError` instead; see above.
* **The robot dropped the command**: another source holds control. The Cockpit outranks a
paired gamepad, which outranks udp, which outranks livekit, and a paired gamepad holds
control while idle. Unpair it and try again.
* **The robot reports STAND but is not armed**: it is not upright. The firmware arms after
0.5 s of STAND under 30 degrees of tilt; `robot.armed` reads the test.
The Robot Tipped Over After `stand()` in MOVE [#the-robot-tipped-over-after-stand-in-move]
`stand()` is sent from any robot mode. STAND has no balance loop, so a free-standing robot
asked to stand from MOVE tips over. Before `stand()` in MOVE, hang the robot from its gantry
hook or seat it on a stool or bench; `rest.py` asks first. To stand still after a walk, call
`balance()`: zero velocity keeps the robot in MOVE, balancing in place. See
[Bringing the Robot to Rest](/sdk/safety#bringing-the-robot-to-rest).
`set_velocity` Returned but the Robot Did Not Walk [#set_velocity-returned-but-the-robot-did-not-walk]
In order of likelihood:
. The hold was cut short: with `wait=False`, `set_velocity` returns at once, and the next
verb or the end of the `with` block ends the hold. Keep the connection open for the
duration, or leave `wait` at its default so the call returns after the hold.
. The robot is not in MOVE. `set_velocity()` is sent in any robot mode: in DAMP the robot
drops it, and in a STAND that is not armed it leaves the robot in STAND. MOVE is
entered only from STAND held upright for 0.5 s, and an early velocity is neither refused
nor reported. Check `state.mode` and `robot.armed`, and call `stand()` then `balance()`
first.
. Another source holds control; see above. The state stream does not say who does.
A `stand()` or `damp()` Did Nothing [#a-stand-or-damp-did-nothing]
A loop is streaming `trajectory()` setpoints and the next setpoint overwrote the verb. A
`stand()` sent into the loop is overwritten this way. A `damp()` usually holds, because
the robot drops trajectories once it reports DAMP, but a setpoint that arrives
first still wins. Stop the loop, then send the verb. A `set_joints()` does not have this
problem: any verb ends it.
`StateStaleError` [#statestaleerror]
The robot went quiet for longer than `stale_after` (default: the link timeout, 2 s) during
`wait_until()` or a command's wait. Treat it as absent, not slow: the connection dropped,
the robot's software stopped, or it is reporting to a different machine. `LinkLostError`
follows. A stream that is merely late before a command is sent is `stale_state`, waited out
and then a `NotReadyError`.
`LinkLostError` [#linklosterror]
No state for 2 s. The SDK sent zero velocity if it held one, and every verb now raises this until you
`close()` and `connect()` again. On udp and hybrid, the robot also zeroes the velocity 2 s
after the last command it received.
`NotConnectedError` [#notconnectederror]
`robot.get_state()` or `damp()` before the robot reported (after
`connect(require_state=False)`), or any verb after `close()`. `close()` during a `set_velocity(..., wait=True)` raises this in the waiting
thread.
`UnsupportedError` [#unsupportederror]
This connection does not carry that capability. `camera`, `microphone` and `speaker` need
`hybrid` or `livekit`; on those, a capability is claimed only from a track that arrived
within `media_timeout` of connecting. Gate with `robot.has("camera")`.
`ImportError` Naming Pillow, numpy or OpenCV [#importerror-naming-pillow-numpy-or-opencv]
`Frame.to_jpeg`, `to_numpy`, `Clip.save_frames` and `Clip.save_mp4` need those packages at
call time. The SDK does not depend on them; install the one named.
Every `Sent.wait_outcome()` Returns `Unknown` [#every-sentwait_outcome-returns-unknown]
The robot reports no per-command verdict, so `Unknown` is what every command gets and
`on_refused` never fires. `Unknown` is neither success nor refusal: read the effect with
`wait_until` and `robot.get_state()`, or let `stand()`, `balance()` and `damp()` wait for it.
Velocity Is Slower Than Asked [#velocity-is-slower-than-asked]
Check `sent.clamped` and `sent.command`. The SDK clamps to `Limits`, whose defaults are the
firmware caps of 0.4 m/s and 0.8 rad/s; a robot does not walk faster than that. A tighter
limit came from `Robot(limits=)`, `MENLO_LIMITS`, or the saved robot's `limits`.
Two Sessions Keep Disconnecting Each Other [#two-sessions-keep-disconnecting-each-other]
Both joined the room with the same identity. With `ManagerConfig`, each session gets its own
unless you set the same `label`; with `LiveKitConfig`, one token is one participant, so mint
two.
Related [#related]
* [Safety](/sdk/safety): what each layer stops, what the SDK reports, and your own guard
* [Troubleshooting the Robot](/asimov/1/operate/troubleshooting): problems that are not the SDK's
# Without the SDK
The robot's LiveKit room carries everything the `livekit` connection mode uses: state and
commands as data messages, the camera and the microphone as tracks. Any LiveKit client can
join it. This page shows how, in Python, without `menlo-sdk`; the scripts are in the
`livekit_raw/` folder of the [examples](/sdk/examples#without-the-sdk).
```bash
pip install livekit asimov-protocol
export MENLO_CREDENTIAL=... # an SDK credential from Asimov Manager
```
`asimov-protocol` holds the protobuf messages; `RobotState` and `RobotCommand` are what the
SDK sends and reads. Set `MANAGER_URL` in `manager_token.py` to your Asimov Manager
address. The role of the credential applies: an Observe credential can read state and the
tracks but its commands are dropped ([Connection Modes](/sdk/connection-modes#livekit)).
No limits, no link watchdog, no zero velocity on exit and no fence between commands. Run
`send_commands.py` only with the robot hanging from its gantry hook, and keep Asimov
Manager open at the **E-Stop**. Work through
[Before You Run a Script](/sdk/safety#before-you-run-a-script) first.
A Join Token [#a-join-token]
Asimov Manager issues join tokens at `POST /api/livekit/token` to a request that carries an
SDK credential as a Bearer token. The answer names the LiveKit `url`, the `room`, the
`token`, your participant `identity` and the credential's `role`. `manager_token.py` fetches
one; it refuses redirects, so the credential is never forwarded to another host, and it
rewrites a `localhost` LiveKit URL to the manager's host, which is what a robot that runs
its own LiveKit server reports.
```python lineNumbers=20
class NoRedirect(urllib.request.HTTPRedirectHandler):
"""Refuse every redirect: urllib would send the credential on to the new address."""
def redirect_request(self, *args: Any, **kwargs: Any) -> None:
return None
def fetch_grant(manager: str = MANAGER_URL, credential: str = CREDENTIAL) -> dict[str, Any]:
"""The LiveKit URL, the robot's room and a join token, from Asimov Manager."""
if not credential:
sys.exit("Set MENLO_CREDENTIAL to an SDK credential from Asimov Manager.")
request = urllib.request.Request(
manager.rstrip("/") + "/api/livekit/token",
data=b"{}",
method="POST",
headers={"Authorization": f"Bearer {credential}", "Content-Type": "application/json"},
)
with urllib.request.build_opener(NoRedirect).open(request, timeout=5) as response:
grant: dict[str, Any] = json.load(response) # url, room, token, identity, role
# The URL is the one the robot uses itself; localhost there is Asimov Manager's host.
url = urlsplit(grant["url"])
if url.hostname in ("localhost", "127.0.0.1"):
host = urlsplit(manager).hostname or ""
netloc = host if url.port is None else f"{host}:{url.port}"
grant["url"] = url._replace(netloc=netloc).geturl()
return grant
```
The other scripts in the folder import `fetch_grant()` from this file.
Read State [#read-state]
The robot publishes `RobotState` on the `state` data track at the firmware's state rate.
`read_state.py` joins, decodes each message with `RobotState.FromString` and prints the mode,
protocol version and sequence number for 5 s:
```python lineNumbers=18
async def print_states(track: rtc.RemoteDataTrack) -> None:
async for frame in track.subscribe():
state = asimov_state_pb2.RobotState.FromString(bytes(frame.payload))
mode = asimov_common_pb2.ControlMode.Name(state.current_mode)
print(f"{mode} protocol {state.protocol_version} sequence {state.sequence}")
async def main() -> None:
grant = fetch_grant()
room = rtc.Room()
readers: set[asyncio.Task[None]] = set()
def on_data_track(track: rtc.RemoteDataTrack) -> None:
if track.info.name == "state":
task = asyncio.ensure_future(print_states(track))
readers.add(task) # keep a reference so the task is not collected
task.add_done_callback(readers.discard)
room.on("data_track_published", on_data_track)
await room.connect(grant["url"], grant["token"])
try:
await asyncio.sleep(SECONDS)
finally:
await room.disconnect()
```
`current_mode` is the firmware's mode enum, `error_flags` the latched fault bits, and
`active_alerts` the current alerts. The field list is in [The Protocol](/sdk/reference#the-protocol).
Send a Command [#send-a-command]
Commands are `RobotCommand` messages published as reliable data on the `commands` topic.
Each carries `protocol_version = 1`, a `sequence` you increment, and `timestamp_us`. The
robot drops a command whose protocol version it does not speak, and reports nothing.
`send_commands.py` keeps a guard of its own, like `guard.py` in the SDK examples: it listens
to state for 2 s and sends nothing when the state is stale, the protocol version is wrong, a
critical alert or `error_flags` is set, the battery is protecting itself or below 20 %, or an
actuator is at 60 °C or more. The limits are constants at the top of the file; change them as
you need. If the robot is in DAMP, it sends one STAND and exits.
```python lineNumbers=85
reason = why_not_stand(*latest) if latest else "no state received"
if reason is not None:
print("not sending STAND:", reason)
return 1
payload = command(asimov_common_pb2.CONTROL_MODE_STAND)
await room.local_participant.publish_data(payload, reliable=True, topic="commands")
```
A velocity is the same message with `set_velocity` filled in. Without the SDK, nothing re-sends
it, so send zero velocity yourself before you leave the room. Asimov Edge stops a held
velocity when a participant sends zero or leaves the room
([Watchdogs](/sdk/safety#watchdogs)).
Camera [#camera]
The camera is the robot's video track. `camera.py` subscribes, takes one frame from a
`VideoStream`, converts it to RGB and writes `frame.ppm`:
```python lineNumbers=19
async def save_one_frame(track: rtc.Track, done: asyncio.Future[str]) -> None:
stream = rtc.VideoStream(track)
async for event in stream:
rgb = event.frame.convert(rtc.VideoBufferType.RGB24)
header = f"P6 {rgb.width} {rgb.height} 255\n".encode()
Path(PATH).write_bytes(header + bytes(rgb.data))
done.set_result(f"saved {PATH}, {rgb.width}x{rgb.height}")
break
await stream.aclose()
async def main() -> None:
grant = fetch_grant()
room = rtc.Room()
done: asyncio.Future[str] = asyncio.get_running_loop().create_future()
tasks: set[asyncio.Task[None]] = set()
def on_track(track: rtc.Track, *_: object) -> None:
if track.kind == rtc.TrackKind.KIND_VIDEO and not tasks:
tasks.add(asyncio.ensure_future(save_one_frame(track, done)))
room.on("track_subscribed", on_track)
await room.connect(grant["url"], grant["token"])
try:
print(await asyncio.wait_for(done, TIMEOUT_S))
finally:
await room.disconnect()
```
Audio [#audio]
The microphone is the robot's audio track. `audio.py` reads an `AudioStream` for a few
seconds and writes `microphone.wav`:
```python lineNumbers=21
async def record(track: rtc.Track, done: asyncio.Future[str]) -> None:
stream = rtc.AudioStream(track)
chunks: list[bytes] = []
rate = channels = 0
async for event in stream:
frame = event.frame
rate, channels = frame.sample_rate, frame.num_channels
chunks.append(bytes(frame.data))
if sum(len(c) for c in chunks) >= SECONDS * rate * channels * 2:
break
await stream.aclose()
with wave.open(PATH, "wb") as wav:
wav.setnchannels(channels)
wav.setsampwidth(2) # 16-bit samples
wav.setframerate(rate)
wav.writeframes(b"".join(chunks))
done.set_result(f"saved {PATH}, {SECONDS:.0f} s at {rate} Hz")
async def main() -> None:
grant = fetch_grant()
room = rtc.Room()
done: asyncio.Future[str] = asyncio.get_running_loop().create_future()
tasks: set[asyncio.Task[None]] = set()
def on_track(track: rtc.Track, *_: object) -> None:
if track.kind == rtc.TrackKind.KIND_AUDIO and not tasks:
tasks.add(asyncio.ensure_future(record(track, done)))
room.on("track_subscribed", on_track)
await room.connect(grant["url"], grant["token"])
try:
print(await asyncio.wait_for(done, SECONDS + TIMEOUT_S))
finally:
await room.disconnect()
```
To play sound on the robot, publish an audio track of your own; the robot plays it on its
speaker.
Related [#related]
* [Connection Modes](/sdk/connection-modes): the `livekit` mode and the SDK credential
* [Camera and Audio](/sdk/media): the same tracks through the SDK
* [Safety](/sdk/safety): what the SDK adds on top of the wire, and what it does not
* [SDK Reference](/sdk/reference#the-protocol): the protocol messages
* [Robot API](/asimov/1/program/api): the HTTP API of Asimov Manager
# Overview
Asimov 2 [#asimov-2]
V2 concepts, full specs & shipping date unknown.
Interested? [Talk to us](https://tally.so/r/J9O5BJ)
# Overview
Digital Asimov is a free, browser-based digital twin of the Asimov robot. Drive it, watch its telemetry, and explore what the robot can do — all from a browser tab, with no hardware to set up.
It's the quickest way to get a feel for Asimov: a fun test-drive of the robot and a zero-setup sandbox before hardware is available.
Access it at [try.menlo.ai](https://try.menlo.ai) — sign in and a robot is created for you automatically.
A faithful digital twin [#a-faithful-digital-twin]
Digital Asimov isn't an idealized physics demo — it mirrors the real robot:
* **Real actuator models** measured from physical hardware, not idealized physics.
* **The same control stack** a physical Asimov runs, so what works here works on hardware.
* **The same telemetry**, streamed over the same wire format as a physical robot.
* **FPV and third-person camera** views, rendered as a live video track.
What you can do [#what-you-can-do]
# Quickstart
Drive a Digital Asimov robot in your browser in under a minute — no hardware, no install.
Open the simulator [#open-the-simulator]
Go to [try.menlo.ai](https://try.menlo.ai) and sign in with Google.
A robot is created for you automatically — no setup required.
Start the robot [#start-the-robot]
The simulator launches with the robot running. The cockpit interface appears immediately.
Control the robot [#control-the-robot]
Use keyboard controls for direct teleop:
| Key | Action |
| ------- | -------------- |
| `W` | Forward |
| `S` | Backward |
| `A` | Strafe left |
| `D` | Strafe right |
| `Q` | Turn left |
| `E` | Turn right |
| `Space` | Emergency stop |
Each keypress fires one movement command immediately. A green **Command sent** flash confirms each command; a red **Command failed** flash means the session rejected it.
For physical robots, see the [Asimov API](/asimov/1/program/api).
# Telemetry
Once the robot is running, telemetry flows into three panels across the settings page and cockpit.
Event log [#event-log]
Timestamped stream of everything the robot reports: connection state, firmware messages, alert-rule fires, and errors. Each entry shows a timestamp, message, and source; severity is color-coded (info, warn, error).
Use it as the first stop when something looks off — a dropped session, an unresponsive joint, or an alert you didn't expect will all leave a line here.
Joints [#joints]
Per-joint table with three columns:
| Column | Unit | Notes |
| ----------- | ---- | --------------------- |
| Position | rad | Current joint angle |
| Current | A | Actuator current draw |
| Temperature | °C | Turns red above 70°C |
Click the bell icon on any row to create an alert rule for that joint. When a reading crosses your threshold, the rule fires a browser notification and writes a line to the event log.
Subsystem Health [#subsystem-health]
One status dot per on-robot subsystem:
| Subsystem | Covers |
| --------- | ----------------------------------------- |
| FW Link | Firmware link to the actuator controllers |
| Speaker | On-board speaker |
| Camera | On-board camera |
| Mic | On-board microphone |
| BLE Radio | Bluetooth-LE radio |
| Cloud | Cloud connectivity |
The **Alerts badge** next to the robot name summarizes this panel — `NO ALERTS` when everything is green, `NN ALERTS` with an unread dot when a subsystem is reporting an error. Click the badge for a popover listing each failure.
Subsystem Health only reports real values for physical Asimov robots. Platform UI support for physical robots is coming soon — until then, the panel renders as a layout preview.
# Assembly Manual
This assembly manual provides a detailed guide to building the
Asimov 0 legs, including the required tools and parts, the assembly
process for each module, when and how to perform debugging and
testing during assembly, and the basic steps for operating the
robot.
1\. Tool Lists 🛠️ [#1-tool-lists-️]
Allen keys (M2 to M6) [#allen-keys-m2-to-m6]
Allen keys (M2 to M6) are used to tighten and loosen hex socket screws during assembly, and this build requires sizes ranging from M2 to M6.
Soldering Iron [#soldering-iron]
A soldering iron is required to connect the electronic components.
Hot air gun [#hot-air-gun]
A heat air gun will be used to heat the string. Any standard hot air gun will work, so you can use the one that is most convenient for you.
Wire stripper [#wire-stripper]
A wire stripper is used to remove insulation from the ends of wires so they can be soldered, crimped, or connected to terminals during humanoid assembly. Any standard wire stripper suitable for the wire gauges used in this build.
Label Maker (Optional) [#label-maker-optional]
Useful for labeling wires, connectors, and modules to make assembly and debugging easier.
CAN-to-USB Adapter (Optional) [#can-to-usb-adapter-optional]
A CAN-to-USB adaptor is important for post-assembly debugging, since it allows direct connection to the actuator over CAN bus for testing and verification. You can buy from [Waveshare](https://www.waveshare.com/usb-can-b.htm).
Multimeter [#multimeter]
A multimeter is used to measure electrical values such as voltage, current, resistance, and continuity. Most multimeters support these basic functions, so any standard one can usually be used.
Crimp tool [#crimp-tool]
A crimp tool is used to attach connectors securely to wires.
2\. Get the parts 📦 [#2-get-the-parts-]
Before you start building, gather all the required components first. Please review the [BOM and sourcing path](/asimov/1/build/assembly-preparations) to make sure you have the required parts.
> Tip: Group the parts before you begin, such as screws, bolts, wires, actuators, connectors, and mechanical parts. This will help you build faster.
Also make sure you have a basic understanding of the essential parts covered in these hardware chapters:
[1. Frame and structural components](/asimov/0/hardware/frame-and-structural-components)
[2. Joint design and actuation](/asimov/0/hardware/joint-design-and-actuation)
[3. 3D printing and fabrication](/asimov/0/hardware/3d-printing-and-fabrication)
3\. Building the robot 🤖 [#3-building-the-robot-]
Release Soon !
# Before You Start
This is the right place to start if you are deciding whether Asimov 0 is for you, evaluating the build, or preparing to work through this manual.
Asimov 0 is an open-source bipedal legs robot. This manual covers how the legs are designed, assembled, and brought from simulation work to real hardware locomotion. It is part of the broader Asimov project, which you can explore at [asimov.inc](https://asimov.inc).
Expect ambitious design choices, incomplete edges, and a build process that rewards technical judgment.
What this is [#what-this-is]
This manual is focused on `Asimov 0`, specifically:
* the leg hardware architecture
* the fabrication and assembly workflow
* bring-up and operating context for the legs
* the simulation and reinforcement-learning stack used for locomotion
What this is not [#what-this-is-not]
This is not a complete full-body humanoid operating guide.
It is also not a lightweight consumer build booklet. The current emphasis is the lower body, the technical decisions behind it, and the workflow required to take it from components to working hardware.
Core resources [#core-resources]
If you want the source-of-truth artifacts behind this manual, start here:
* [Asimov 0 repository](https://github.com/asimovinc/asimov-v0): the main source repository for the robot, including the published actuator list, mechanical assets, and supporting project files.
* [Asimov 0 sim model](https://github.com/asimovinc/asimov-v0/tree/main/sim-model): the simulation-model directory used as the basis for simulator-side work.
* [Asimov 0 lower-body 3D file](https://static.asimov.inc/ASV0_LowerBody.HTML): browser-viewable lower-body geometry for inspecting the leg design at a high level.
If you are trying to understand whether Asimov 0 matches your needs, those three links usually answer the first serious questions: what the robot is, how the legs are packaged, and where the simulation artifacts live.
Who this is for [#who-this-is-for]
This manual is intended for readers who want one of two things:
* to understand how Asimov 0 is designed and why specific hardware and control choices were made
* to build, assemble, and validate the legs themselves
It is a good fit for robotics engineers, advanced hobbyists, research teams, and technical builders who are comfortable working across mechanics, electronics, embedded systems, and simulation.
Expected effort [#expected-effort]
Asimov 0 is a serious hardware project, not a one-hour weekend kit.
You should expect meaningful time in:
* procurement and part preparation
* fabrication and finishing
* mechanical assembly
* wiring and electronics checks
* bring-up, debugging, and calibration
* simulation and policy validation before any walking attempt
The exact build time and cost will depend on whether you are sourcing parts independently or starting from a kit, how much fabrication you do yourself, and how much prior robotics experience you already have.
The DIY Kit is the faster path if your goal is the broader full-body robot. If your goal is specifically `Asimov 0` legs, treat the kit as optional and treat this manual as the primary technical reference.
Safety [#safety]
Treat Asimov 0 as powered electromechanical hardware, not as a toy.
Before working on the robot, make sure you have:
* a stable workspace with room to support or suspend the robot safely
* a plan for power isolation and emergency shutdown
* basic electrical test tools such as a multimeter
* a controlled bring-up process for actuators, wiring, and motion tests
Do not attempt first power-on, homing, or motion tests casually. Early mistakes in wiring, joint direction, or configuration can damage components or create unsafe motion.
Initial bring-up and locomotion tests should only be done in a controlled setup with the robot restrained, supported, or otherwise prevented from falling unexpectedly.
What success looks like [#what-success-looks-like]
For most builders, success should be evaluated in stages:
. You understand the system architecture and have the required parts, tools, and workspace.
. The hardware is assembled correctly and passes basic electrical and mechanical checks.
. The robot can be powered, homed, and verified safely.
. The software and simulation stack are configured well enough to validate the control path.
. The robot reaches stable real-world locomotion behavior on hardware.
The right first milestone is usually not “make it walk immediately.” The right first milestone is a clean, safe, verifiable bring-up.
Where to go next [#where-to-go-next]
* Read the [Overview](/asimov/0/overview) for the structure of the manual and the recommended reading order.
* Go to [Hardware Design](/asimov/0/hardware) if you want to understand the robot before sourcing parts.
* Visit [asimov.inc](https://asimov.inc) for the broader project overview.
* Visit the [Asimov DIY Kit](https://asimov.inc/diy-kit) if you are evaluating the broader full-body hardware package.
# Introduction
This page explains how the manual is organized and what to read first.
If you are evaluating the project or starting from scratch, begin with [Before You Start](/asimov/0). That page covers scope, audience, effort, safety, and the core project links.
This is the earliest public version of the Asimov manual. It is being released in stages, and several sections will continue to expand as the documentation matures.
The manual is organized into three working sections:
* [Hardware Design](/asimov/0/hardware) explains how the robot is designed, including the electrical and mechanical structure, major components, and joint architecture.
* [Assembly Manual](/asimov/0/assembly-manual) covers the practical build process, required tools and parts, and the path toward bring-up and operation.
* [Locomotion Control](/guides/locomotion-training) documents the simulation and reinforcement-learning workflow used to train and deploy walking behavior.
Recommended reading order [#recommended-reading-order]
If you are new to the project, read the manual in this order:
. [Before You Start](/asimov/0)
. [Hardware Design](/asimov/0/hardware)
. [Assembly Manual](/asimov/0/assembly-manual)
. [Locomotion Control](/guides/locomotion-training)
That sequence takes you from orientation and decision-making, to system understanding, to build execution, to simulation and deployment.
Asimov - Here be Dragons is now available for pre-order at [asimov.inc/diy-kit](https://asimov.inc/diy-kit).
# Overview
This page explains how the manual is organized and what to read first.
If you are evaluating the project or starting from scratch, begin with [Before You Start](/asimov/0). That page covers scope, audience, effort, safety, and the core project links.
This is the earliest public version of the Asimov manual. It is being released in stages, and several sections will continue to expand as the documentation matures.
The manual is organized into three working sections:
* [Hardware Design](/asimov/0/hardware) explains how the robot is designed, including the electrical and mechanical structure, major components, and joint architecture.
* [Assembly Manual](/asimov/0/assembly-manual) covers the practical build process, required tools and parts, and the path toward bring-up and operation.
* [Locomotion Control](/guides/locomotion-training) documents the simulation and reinforcement-learning workflow used to train and deploy walking behavior.
Recommended reading order [#recommended-reading-order]
If you are new to the project, read the manual in this order:
. [Before You Start](/asimov/0)
. [Hardware Design](/asimov/0/hardware)
. [Assembly Manual](/asimov/0/assembly-manual)
. [Locomotion Control](/guides/locomotion-training)
That sequence takes you from orientation and decision-making, to system understanding, to build execution, to simulation and deployment.
Asimov - Here be Dragons is now available for pre-order at [asimov.inc/diy-kit](https://asimov.inc/diy-kit).
# Overview
This collection documents the locomotion stack used to take Asimov from simulation to real-world walking. The current material is centered on the legs-only platform: 12 actuated leg joints with 2 passive toe joints. The same design principles are intended to carry forward into the full-body controller.
The locomotion stack is organized around one core idea: successful transfer is determined less by policy novelty and more by whether the policy sees the same type, timing, and quality of data in simulation that it will see on hardware.
The chapters in this collection are organized as follows:
* [Understanding Your Simulation Environment](/guides/locomotion-training/understanding-your-simulation-environment) defines the simulator assumptions, actuator interfaces, timing model, and the limits of a purely physics-only view of sim2real.
* [Reinforcement Learning for Locomotion](/guides/locomotion-training/reinforcement-learning-for-locomotion) describes the policy formulation, actor and critic observations, and the control philosophy used for transfer.
* [Deep Dive: System Identification](/guides/locomotion-training/reinforcement-learning-deep-dive-system-identification) documents the hardware-to-simulation mapping, actuator parameters, armature values, joint constraints, and other identified quantities that matter for stable behavior.
* [Simulation Training Environment](/guides/locomotion-training/reinforcement-learning-simulation-training-environment) covers action and observation interfaces, control rates, delays, filters, and environment structure.
* [Reward Design](/guides/locomotion-training/reinforcement-learning-reward-design) explains which rewards were kept, which ones were removed, and which ones were modified for Asimov hardware.
* [Domain Randomization](/guides/locomotion-training/reinforcement-learning-domain-randomization) describes the targeted randomization strategy used for sim2real transfer.
* [Policy Deployment](/guides/locomotion-training/reinforcement-learning-policy-deployment) documents the real firmware loop, processor-in-the-loop validation, and the practical issues encountered during bring-up on hardware.
This collection should be read together with the hardware chapters, especially the discussions of the parallel ankle mechanism and passive toes in [Joint Design and Actuation](/asimov/0/hardware/joint-design-and-actuation).
# Deep Dive: System Identification
This chapter documents the hardware-to-simulation quantities that were identified or constrained for stable locomotion transfer.
1\. Hardware mapping comes first [#1-hardware-mapping-comes-first]
Before tuning rewards or training parameters, the joint-level hardware mapping must be correct.
For Asimov legs, the following items were especially important:
* the ankle is not directly driven
* ankle pitch and roll are produced through a parallel mechanism
* toe behavior is passive and spring-driven
* leg joints use different actuator families with different reflected inertia and torque-speed limits
Errors in this mapping produced unstable or seemingly random policy behavior.
2\. Armature as reflected rotor inertia [#2-armature-as-reflected-rotor-inertia]
In this stack, joint armature should not be interpreted as the literal electrical armature. It is used as a reflected inertia term that captures how the rotor and gearbox appear at the joint.
This distinction matters because armature strongly affects closed-loop behavior and stability.
Representative identified values are:
| Joint family | Example value | Notes |
| ------------ | ------------- | ----------------------------------------------------------- |
| hip pitch | `0.095625` | From actuator datasheet and transmission mapping |
| knee | `0.0339552` | From actuator datasheet and transmission mapping |
| ankle | `0.0565056` | Doubled to reflect two actuators driving the parallel ankle |
The ankle value required special treatment because pitch and roll are driven by two actuators through the RSU ankle mechanism.
3\. KP/KD consistency between sim and hardware [#3-kpkd-consistency-between-sim-and-hardware]
Even with calculated KP/KD values, the robot exhibited vibration on startup. After analysis, the root cause was not a hardware limitation — the real actuator controllers worked fine. The problem was that the **simulation KP/KD values produced an underdamped system**, and the policy learned to behave accordingly.
The policy is simply an MLP. Its job is to model some non-linear function based on the data provided to it and how its weights get updated. When trained on data from an underdamped simulated system, the policy learned to behave like an underdamped controller. And how does an underdamped control system respond to an impulse? It oscillates.
This was verified mathematically: the policy's output behavior matched the impulse response of an underdamped second-order system.
The failure chain is:
. simulation KP/KD values produce underdamped dynamics
. the policy trains on this data and learns to behave like an underdamped controller
. on real hardware, the gains are fine — but the policy's learned behavior is already underdamped
. the policy's corrections overshoot, and each overshoot triggers a larger correction on the next cycle, exciting sustained oscillation
This realization was critical because it reframed the locomotion problem:
> How do I make the domain of data between sim and real match as closely as possible?
The practical lesson is not just about constraining gains to hardware limits — it is about ensuring the simulated dynamics produce training data that matches the real system's response characteristics. If the sim data domain diverges from the real data domain, the policy will learn behavior that does not transfer, regardless of whether the individual parameter values are physically plausible.
4. Actuator model details that mattered [#4-actuator-model-details-that-mattered]
The actuator model includes more than simple PD control. The simulation stack models:
* per-joint stiffness and damping
* effort limits
* speed-torque saturation
* reflected inertia through armature
* static and dynamic friction
* explicit action delay
This richer actuator model was a significant part of the sim2real improvement.
Representative actuator parameters for the legs stack include:
| Parameter | Example value | Note |
| ----------------- | ------------- | ---------------------------------------- |
| stiffness | `65.0` | chosen as a safe deployable value |
| damping | `5.0` | tuned to match real system response |
| effort limit | `39.40` | peak torque for the modeled joint family |
| saturation effort | `120.0` | speed-torque saturation behavior |
| velocity limit | `12.57 rad/s` | from actuator specification |
| friction static | `1.30` | static friction term |
| friction dynamic | `0.100` | Coulomb-like dynamic friction term |
The simulated actuator path is then wrapped in an explicit delay model with `delay_min_lag=0` and `delay_max_lag=1`.
5\. Delay is part of identification [#5-delay-is-part-of-identification]
Actuator delay was not treated as a generic nuisance term. It was modeled from the observed timing behavior of the real firmware and communication path.
The training model therefore includes:
* action delay on the actuator path
* grouped observation delay on the sensing path
* real CAN timing structure rather than perfectly synchronized joint state
These delays are part of the identified system, not just regularization noise.
This same reasoning also motivated the move away from an overly pristine built-in actuator interpretation toward a control path that better reflected what the policy would actually see at IO rate.
6. Toe model identification [#6-toe-model-identification]
The toe joint is passive, but it still affects whole-body stability through contact and push-off.
The simulator therefore needs:
* toe stiffness
* toe damping
* toe limits
* toe collision geometry
* toe-ground contact behavior
In practice, toe resistance had to be increased relative to early assumptions because insufficient toe support caused the policy to ignore the toe during learning.
Toe state was exposed to the critic, not the actor. This allowed training to capture the stabilizing effect of the toe without introducing a deploy-time dependency on unmeasured joint state.
7\. Collision geometry is also system identification [#7-collision-geometry-is-also-system-identification]
Contact behavior is highly sensitive to geometry. The locomotion environment therefore replaced detailed mesh collision with simpler capsule-based foot and toe geometry.
This choice improved determinism and reduced the risk of learning artifacts from unstable mesh contact.
The identified contact model includes:
* multiple foot and toe capsules
* explicit foot-ground contact sensing
* toe contact sensing
* tuned friction and contact dimensions on foot and toe geoms
The final contact configuration emphasized repeatability:
| Contact setting | Value / choice |
| ------------------- | ----------------------------------- |
| contact primitive | capsules instead of mesh collision |
| foot / toe friction | `0.6` |
| contact dimension | `condim=3` on foot and toe geometry |
| capsule radius | approximately `12 mm` |
In practice, multiple heel, midfoot, and toe capsules were used so the support polygon was more stable than a single coarse collision shape.
8\. Soft limits and deployable ranges [#8-soft-limits-and-deployable-ranges]
The policy is not trained to use the full hard-stop hardware range. Instead, training uses soft joint limits, typically at `0.9` of the hardware range.
This reduces:
* hard-stop impacts
* unrealistic exploitation of boundary states
* deployment-time shock loads near limit boundaries
The soft-limit factor used in training was approximately `0.9` of the hardware range.
9\. Geometry errors can invalidate learning [#9-geometry-errors-can-invalidate-learning]
System identification also includes checking the geometry itself. One important example was toe alignment: when the toes were accidentally tilted relative to the intended flat contact pose, the policy stopped learning effective forward-balance recovery.
This is a useful reminder that a locomotion policy can fail even when gains and rewards are reasonable, simply because the physical model is not internally consistent.
# Domain Randomization
This chapter describes the domain randomization strategy used for Asimov locomotion.
1\. Targeted, not broad [#1-targeted-not-broad]
The randomization strategy is intentionally selective. The goal is not to randomize every quantity in the simulator. The goal is to randomize the quantities that are known to vary between simulation and hardware.
This chapter should therefore be read with the following principle in mind:
> Randomize what is known to vary. Do not randomize what has already been measured with sufficient accuracy.
2\. Quantities that are randomized [#2-quantities-that-are-randomized]
Representative randomized terms include:
| Parameter | Range | Reason |
| ----------------------------- | ---------------------------------------------- | ----------------------------- |
| encoder zero offset (`qpos0`) | `+/-0.02 rad` | Calibration error |
| PD gains | `x0.9 - x1.1` | Actuator response variation |
| toe stiffness | `3.5 - 5.5 Nm/rad` | Spring variation |
| foot friction | `1.0 - 1.5` | Surface variation |
| observation delay | `0-2` steps | CAN timing jitter |
| action delay | `0-1` steps | Command latency |
| push disturbance | `+/-0.5 m/s` class disturbances | External perturbations |
| reset base orientation | `yaw+/-180°, pitch+/-0.15 rad, roll+/-0.1 rad` | Initial orientation variation |
| joint velocity noise | `+/-0.1 rad/s` | Encoder velocity noise |
| IMU angular velocity noise | `+/-0.01 rad/s` | Gyro measurement noise |
These randomizations are tied directly to known sources of mismatch.
3\. Quantities intentionally not randomized [#3-quantities-intentionally-not-randomized]
Some quantities are intentionally left fixed during initial training.
| Parameter | Reason |
| ------------ | --------------------------------------------------------------------- |
| body mass | broad randomization reduced learning stability during initial walking |
| link lengths | CAD and URDF geometry were already close to hardware |
| gravity | deployment environment does not vary meaningfully |
This prevents training from spending capacity on unlikely or unnecessary variability.
4\. Delay randomization is not generic noise [#4-delay-randomization-is-not-generic-noise]
Observation and actuator delay randomization are especially important in this stack. These delays are not abstract robustness noise — they reflect the real CAN polling structure and firmware timing described in [Deep Dive: System Identification](/guides/locomotion-training/reinforcement-learning-deep-dive-system-identification). The randomization ranges in the table above correspond directly to the measured variation in those timing paths.
5\. Contact-side randomization [#5-contact-side-randomization]
Foot friction and contact-dependent terms are also randomized because walking quality depends strongly on floor condition and contact consistency.
These terms help the policy remain usable across:
* slightly different surfaces
* moderate contact-model mismatch
* unit-to-unit variation in toe and foot response
6\. Randomization still depends on an accurate base model [#6-randomization-still-depends-on-an-accurate-base-model]
Domain randomization is not a substitute for system identification.
The stack first requires:
* correct hardware mapping
* realistic actuator parameters
* stable contact geometry
* deployable observation design
Only after those are in place does targeted randomization improve robustness in a meaningful way.
# Reinforcement Learning for Locomotion
This chapter describes the policy design used for Asimov locomotion and the reasoning behind its observation interface.
*Figure: Asimov legs during early locomotion development. This image anchors the policy chapter to the real hardware platform the controller was trained for, rather than to a generic humanoid benchmark or a simulator-only setup.*
1\. Locomotion as a data-interface problem [#1-locomotion-as-a-data-interface-problem]
The locomotion policy is a standard feedforward neural network. The central design question is not the novelty of the network itself, but whether the policy receives the correct information at the correct time.
For this reason, the locomotion problem is framed as:
> How can the domain of data in simulation be made to match the domain of data on hardware as closely as possible?
This framing leads to several design choices:
* avoid observations that are unavailable on the robot
* model timing skew and delay explicitly
* give the critic access to privileged training-only information
* treat actuator behavior as part of the learning problem
*Figure: Qualitative walking result from the trained legs policy. The motion shown here is useful as a visual reference for the kind of gait the observation design, critic structure, and actuator model were intended to produce on hardware.*
2\. Actor observations [#2-actor-observations]
The actor is limited to signals that can be produced on the real robot. The policy observation vector has 45 dimensions.
| Observation term | Dimensions | Notes |
| ------------------------- | ---------- | --------------------------------------------------- |
| base angular velocity | 3 | IMU angular velocity |
| projected gravity | 3 | Orientation proxy used instead of ground-truth pose |
| command | 3 | Target `v_x`, `v_y`, `w_z` |
| joint position groups 1-3 | 12 | Grouped by CAN timing |
| joint velocity groups 1-3 | 12 | Grouped by CAN timing |
| previous actions | 12 | Smoothed control history |
The observation design intentionally excludes base linear velocity.
3\. No ground-truth linear velocity [#3-no-ground-truth-linear-velocity]
Many locomotion baselines feed ground-truth base linear velocity into the policy. Asimov does not.
The reason is simple:
* the real robot does not measure ground-truth base velocity
* the robot has an IMU and encoder-derived joint state
* training with unavailable information encourages brittle policies
If the actor depends on an observation that disappears at deployment time, transfer quality degrades immediately.
4\. Asymmetric actor-critic [#4-asymmetric-actor-critic]
Training uses an asymmetric actor-critic structure. The actor is restricted to deployable observations, while the critic receives additional privileged information that improves value estimation.
The critic receives everything the actor sees, plus:
| Privileged term | Dimensions | Purpose |
| -------------------- | ---------- | ----------------------------------- |
| base linear velocity | 3 | Ground-truth motion during training |
| foot height | 2 | Contact and swing-state context |
| foot air time | 2 | Step timing context |
| foot contact | 2 | Binary contact state |
| foot contact forces | 6 | Ground interaction |
| toe joint position | 2 | Passive toe state |
| toe joint velocity | 2 | Passive toe dynamics |
This setup allows the critic to learn from simulator-only signals without forcing the actor to depend on unavailable hardware data.
5\. Why toe state belongs in the critic [#5-why-toe-state-belongs-in-the-critic]
The passive toes affect support, push-off, and recovery from forward pitching. However, they are not actively actuated and are not instrumented like the main leg joints.
Toe state is therefore exposed to the critic only.
This allows the training process to capture the relationship between toe deflection and stability, while still requiring the actor to infer toe behavior indirectly from:
* ankle motion
* IMU state
* body response during stance and push-off
6\. Contact force as privileged information [#6-contact-force-as-privileged-information]
Contact force is also useful during training. It helps the critic evaluate whether the robot is loading the ground in a stable way, even though the actor does not receive direct force measurements as a deployment input.
In practice, this improves:
* stance stability
* push-off behavior
* foot placement quality
The resulting policy does not react to contact changes as aggressively as force-rich commercial systems, but it achieves useful and stable behavior without relying on a direct force-sensing action policy.
7\. Network structure [#7-network-structure]
The policy uses a straightforward multilayer perceptron.
| Network | Structure |
| ------- | --------------------------------------------- |
| Actor | `45 -> 512 -> 256 -> 128 -> 12` |
| Critic | `(45 + privileged) -> 512 -> 256 -> 128 -> 1` |
Additional settings:
* activation: ELU
* observation normalization: enabled
* initial policy noise standard deviation: 1.0
The network design is intentionally simple. Sim2real performance came primarily from the observation and actuation interface, not from architectural novelty.
# Practical Issues in Policy Deployment
This chapter describes how the locomotion policy is deployed and validated on real hardware, and which practical issues mattered most during sim2real transfer.
1\. Processor-in-the-loop validation [#1-processor-in-the-loop-validation]
Before deploying to hardware, the locomotion stack is validated through the processor-in-the-loop path described in [Understanding Your Simulation Environment](/guides/locomotion-training/understanding-your-simulation-environment). This section focuses on the deployment-specific validation that path enables.
2\. Jitter and delay must be exercised before deployment [#2-jitter-and-delay-must-be-exercised-before-deployment]
Actuator timing on paper is not the same as actuator timing in a running system. The deployment path therefore validates controller behavior under injected delay and jitter.
The actuator emulator is used to insert randomized response timing so that the stack can be tested against:
* late actuator responses
* race conditions in the control loop
* protocol parsing issues under load
* timing mismatch between sensor and control computation
This turns delay handling into an integration test rather than an assumption.
Representative injected delay was drawn over a range spanning approximately `0.4 ms` to `2 ms`, which was enough to expose timing assumptions in the firmware and policy loop before hardware tests.
3\. CAN ordering mattered [#3-can-ordering-mattered]
One of the important deployment issues was the ordering of actuator-state data on the CAN path.
Early behavior allowed requests to be issued in sequence while responses could arrive in different order. Even though this is logically acceptable at the communication layer, it created a control problem:
* the policy interpreted stale and reordered state as a real physical deviation
* corrective action was applied too aggressively
* the resulting impulses excited oscillations across the legs
The fix was to make the real IO sampling path behave more like the training environment by sampling actuators at the intended rate and waiting for the expected packet order.
The important lesson is that conceptual correctness at the communication layer is not sufficient. If the data arrives in a different temporal structure than the policy expects, the control loop can still fail.
4\. Oscillation should be treated as a systems problem [#4-oscillation-should-be-treated-as-a-systems-problem]
Severe startup oscillation was not solved by adding more rewards. It was the result of two interacting causes:
. **Underdamped policy behavior** — the simulation KP/KD values produced underdamped dynamics, causing the policy (an MLP) to learn underdamped control behavior that it carried onto the real robot. The full failure chain is documented in [Deep Dive: System Identification](/guides/locomotion-training/reinforcement-learning-deep-dive-system-identification).
. **Stale and reordered actuator-state data** — CAN packet ordering (described in Section 3 above) meant the policy was correcting against state that no longer reflected reality, amplifying the oscillation on every cycle.
Both issues had to be resolved together. Fixing the gains alone was insufficient while the policy was still consuming misordered state, and fixing CAN ordering alone was insufficient while the controller was underdamped.
5\. Re-homing still matters [#5-re-homing-still-matters]
Even with a trained locomotion policy, real-robot alignment before execution remains important.
In practice:
* small asymmetries in the real robot can produce visible drift
* a careful home pose reduces bias before walking
* policy quality should not be judged independently of robot setup quality
This is particularly important for narrow-stance walking where small geometric biases can affect lateral balance.
6\. Resulting deployment behavior [#6-resulting-deployment-behavior]
With the final stack, the legs locomotion policy achieved real-world behaviors including:
* forward walking
* backward walking
* lateral walking
* balance recovery under external pushes
The same underlying policy design was used across these cases, demonstrating that the transfer strategy was robust enough for more than a single scripted motion.
*Figure: Joint trajectory comparison between real deployment and simulation. This comparison is included to show that sim2real success was evaluated not only by visual walking quality, but also by whether the commanded and observed motion patterns remained consistent across both domains.*
7\. Carryover to the full-body stack [#7-carryover-to-the-full-body-stack]
The legs-only deployment work establishes the main ingredients that should carry into the full-body controller:
* the same leg actuator architecture
* the same staggered observation-delay philosophy
* the same privileged toe and contact information for training
* the same targeted randomization strategy
The full-body system introduces more joints and more coordination demands, but the sim2real foundation remains the same.
# Reward Design
This chapter documents the reward design used for Asimov locomotion and the main differences from common open-source baselines.
1\. Reward design was not the main bottleneck [#1-reward-design-was-not-the-main-bottleneck]
The locomotion policy did not become deployable through reward shaping alone. Stable transfer depended more strongly on:
* actuator modeling
* observation timing
* deployable observation design
* real controller constraints
Reward design still matters, but it should be understood as one component of the stack rather than the sole driver of performance.
The practical lesson from the legs stack is that reward changes alone did not solve transfer. The walking policy became deployable only after the actuator model, timing model, and observation interface were brought closer to hardware.
2\. Core rewards kept from existing baselines [#2-core-rewards-kept-from-existing-baselines]
The Asimov reward set was heavily influenced by open-source humanoid locomotion work, especially Booster-style reward structure.
Representative retained terms include:
| Reward | Weight | Purpose |
| ------------------ | -------------------------------- | ------------------------------------------- |
| `tracking_lin_vel` | `+1.0` (base, curriculum-scaled) | Follow commanded linear velocity |
| `tracking_ang_vel` | `+0.5` (base, curriculum-scaled) | Follow commanded yaw rate |
| `orientation` | `-5.0` | Penalize deviation from upright orientation |
| `upright` | curriculum | Maintain stable torso posture |
| `action_rate` | `-1.0` | Smooth action changes |
| `torques` | `-2e-4` | Encourage efficient actuation |
3\. No gait clock [#3-no-gait-clock]
Some locomotion baselines provide an explicit gait phase clock to the policy. Asimov does not.
This choice was made because:
* Asimov kinematics are not identical to baseline robots
* the ankle range is limited by the parallel mechanism
* the policy should discover a gait that fits this hardware rather than follow a hand-imposed gait phase
This makes the policy less prescriptive and more hardware-specific.
4\. Asymmetric pose tolerances [#4-asymmetric-pose-tolerances]
Uniform pose tolerances across all joints are not appropriate for Asimov. The legs use different tolerances depending on the joint and the hardware structure.
Representative walking tolerances are:
| Joint | Typical tolerance |
| ----------- | ----------------- |
| hip pitch | `0.5` |
| hip roll | `0.25` |
| hip yaw | `0.2` |
| knee | `0.5` |
| ankle pitch | `0.2` |
| ankle roll | `0.12` |
The ankle tolerances are tight because the real ankle range is limited.
5\. Narrow-stance stability penalties [#5-narrow-stance-stability-penalties]
Asimov has a narrower stance than many humanoid baselines. This increases lateral balance sensitivity and motivates stronger stability penalties.
Representative terms include:
| Reward | Weight |
| ------------------ | ------- |
| `body_ang_vel` | `-0.08` |
| `angular_momentum` | `-0.03` |
These terms help reduce large pelvis rotation and unstable whole-body motion.
6\. Contact-force limits [#6-contact-force-limits]
The reward set penalizes excessive ground reaction forces.
This serves two purposes:
* it discourages aggressive stomping behavior
* it protects the real robot from unnecessary impact loading
Representative terms include:
| Reward | Weight | Note |
| -------------------------- | ------- | ----------------------------------------------------- |
| `feet_contact_force_limit` | `-5e-4` | penalizes forces above approximately `350 N` |
| `feet_stumble` | `-1.25` | penalizes large horizontal-to-vertical contact ratios |
7\. Air-time reward [#7-air-time-reward]
Asimov legs are light enough to support dynamic walking with noticeable swing and brief unloaded phases. An air-time reward is therefore used to discourage shuffling behavior.
Representative term:
| Reward | Weight |
| ---------- | ------ |
| `air_time` | `+0.5` |
This reward encourages dynamic gait emergence rather than static stepping.
8\. Consolidated reward table [#8-consolidated-reward-table]
The legs policy used a compact reward set rather than a large collection of highly specialized terms.
| Reward | Weight | Role |
| -------------------------- | -------------------------- | ---------------------------------- |
| `tracking_lin_vel` | `+1.0` (curriculum-scaled) | commanded linear velocity tracking |
| `tracking_ang_vel` | `+0.5` (curriculum-scaled) | commanded yaw tracking |
| `orientation` | `-5.0` | penalize orientation deviation |
| `air_time` | `+0.5` | dynamic stepping |
| `action_rate` | `-1.0` | smooth action changes |
| `torques` | `-2e-4` | efficient actuation |
| `pose` | curriculum | posture shaping |
| `upright` | curriculum | torso stability |
| `body_ang_vel` | `-0.08` | pelvis rotation penalty |
| `angular_momentum` | `-0.03` | global stability penalty |
| `self_collisions` | `-1.0` | reject self-contact |
| `feet_stumble` | `-1.25` | discourage unstable foot strikes |
| `feet_contact_force_limit` | `-5e-4` | discourage excessive ground impact |
9\. Practical lesson [#9-practical-lesson]
The most important lesson from this stack is that reward design should remain consistent with the hardware interface.
It is counterproductive to reward behaviors that require:
* unavailable sensors
* unrealistic joint range
* unrealistically fast force response
* contact conditions that the deployed robot cannot reproduce
For Asimov, reward design works best when it reflects the real limitations and affordances of the leg hardware.
# Simulation Training Environment
This chapter documents the training environment used for Asimov locomotion, including policy rate, observation timing, and actuator delay.
1\. Training environment structure [#1-training-environment-structure]
The training stack is organized around a MuJoCo-based environment with:
* 200 Hz physics integration
* 200 Hz IO-side state handling
* 50 Hz policy execution
* delayed actuator and observation paths
* asymmetric actor-critic observations
* passive toe dynamics in the plant model
The environment is intentionally designed to avoid an idealized control path.
In this chapter, `IO-side state handling` means the observation and actuator-side update loop. It carries raw actuator-state timing, observation delay, and related control-path computations before the policy runs.
Representative physics settings inherited by the legs stack include:
| Setting | Value |
| ----------------- | ------- |
| physics timestep | `5 ms` |
| policy decimation | `4` |
| policy rate | `50 Hz` |
| solver iterations | `10` |
2\. Observation timing and grouping [#2-observation-timing-and-grouping]
Joint observations are grouped to reflect the real CAN polling order. This means the policy does not receive all joint states as equally fresh data.
| Observation group | Typical freshness |
| ----------------- | ----------------- |
| group 1 | oldest |
| group 2 | intermediate |
| group 3 | freshest |
Representative delay settings are:
| Group | Delay range |
| ------- | ----------- |
| group 1 | `0-2` steps |
| group 2 | `0-1` steps |
| group 3 | `0` steps |
This grouped delay structure is one of the main sim2real features of the stack.
In implementation terms, the oldest joint group reflects the earliest CAN reads, the middle group reflects intermediate bus timing, and the freshest group reflects the last actuator-state packets available in the loop.
3\. Observation noise [#3-observation-noise]
The observation model includes moderate noise terms that reflect sensor and estimation uncertainty.
Representative values include:
| Quantity | Noise |
| -------------------- | -------------- |
| IMU angular velocity | `+/-0.01` |
| projected gravity | `+/-0.05` |
| joint position | `+/-0.01 rad` |
| joint velocity | `+/-0.1 rad/s` |
The goal is not to flood the policy with noise. The goal is to expose it to realistic sensing quality.
4\. Built-in versus IO-rate actuator computation [#4-built-in-versus-io-rate-actuator-computation]
One practical lesson from early experiments was that a pristine built-in actuator path can expose the policy to cleaner data than the real system will ever produce.
In contrast, the final control path intentionally allowed the policy to experience:
* IO-rate computation at `200 Hz`
* slower policy output at `50 Hz`
* numerical roughness from the actual action and observation update path
This was important because the policy needed to tolerate the same class of stale, imperfect signals it would see during deployment.
5\. Actuator interface [#5-actuator-interface]
The action interface is a joint-space command interface over the 12 actuated leg joints.
Important characteristics:
* DC actuator model with per-joint parameters
* explicit actuator delay
* torque-speed saturation
* friction model
* policy actions applied through a slower policy loop than the physics loop
Actuator delay settings and the full actuator model are documented in [Deep Dive: System Identification](/guides/locomotion-training/reinforcement-learning-deep-dive-system-identification).
The action scaling rule preserved in the stack is:
`action_scale = 0.30 * effort / stiffness`
Another important implementation detail is that actuator damping comes from the controller path rather than from fixed XML damping on actuated joints. Default XML damping and friction-loss terms are removed for those joints to avoid double-counting dissipation.
6\. Contact and toe observables [#6-contact-and-toe-observables]
The environment tracks foot contact state, contact forces, foot air time, and toe position/velocity. These signals are used by the critic and reward system even when not exposed to the deployable actor. The toe model and its role in training are described in [Deep Dive: System Identification](/guides/locomotion-training/reinforcement-learning-deep-dive-system-identification).
Representative contact thresholds:
| Quantity | Threshold |
| -------------------------- | ------------------------------ |
| foot contact observation | `5 N` vertical-force threshold |
| reward-side contact helper | `10 N` threshold |
7\. Commands and operating envelope [#7-commands-and-operating-envelope]
The nominal command envelope for the legs locomotion stack is conservative:
| Command | Range |
| ----------- | ------------- |
| `lin_vel_x` | `(-0.8, 0.8)` |
| `lin_vel_y` | `(-0.6, 0.6)` |
| `ang_vel_z` | `(-0.6, 0.6)` |
This operating envelope is appropriate for early sim2real transfer and hardware bring-up.
8\. Disturbances, resets, and play mode [#8-disturbances-resets-and-play-mode]
The training environment also includes controlled perturbations and reset variation.
Representative settings include:
| Item | Representative setting |
| --------------------------- | -------------------------------------------------------------------- |
| push disturbance timing | every `1-3 s` |
| push magnitude | approximately `+/-0.5 m/s` in planar velocity, `+/-1.5 m/s` in pitch |
| reset yaw perturbation | `+/-pi` |
| reset pitch perturbation | `+/-0.15 rad` |
| reset roll perturbation | `+/-0.1 rad` |
| bad-orientation termination | approximately `45 deg` tilt |
Play mode disables policy corruption and push disturbance while preserving the rest of the deployment-relevant control path.
9\. PPO configuration [#9-ppo-configuration]
The training setup uses PPO with a conventional configuration.
| Parameter | Value |
| ------------------- | ------ |
| learning rate | `1e-3` |
| gamma | `0.99` |
| lambda | `0.95` |
| clip parameter | `0.2` |
| entropy coefficient | `0.01` |
| learning epochs | `5` |
| mini-batches | `4` |
| desired KL | `0.01` |
| max grad norm | `1.0` |
| rollout length | `24` |
The optimizer configuration is not the main differentiator of the stack. The environment fidelity and observation design are more important to transfer quality.
# Understanding Your Simulation Environment
This chapter describes what the locomotion simulator is expected to represent, what it intentionally does not represent, and why those boundaries matter for sim2real transfer.
1\. Simulation is part of the control stack [#1-simulation-is-part-of-the-control-stack]
For Asimov, the simulator is not treated as an isolated physics sandbox. It is treated as one component in a larger control stack that includes:
* the robot kinematic and dynamic model
* actuator behavior and delay
* sensor noise and observation timing
* the firmware path used to compute control-relevant signals
* the policy observation and action interface
This viewpoint is important because many real deployment failures are not caused by large physics mismatches. They are caused by timing skew, stale observations, bus jitter, or control signals that are computed differently in simulation and on hardware.
2\. Scope of the locomotion model [#2-scope-of-the-locomotion-model]
The current locomotion stack is built around the legs-only robot:
* 12 actuated joints across both legs
* 2 passive toe joints
* a parallel ankle mechanism rather than a simple directly driven serial ankle
The simulator uses rigid-body dynamics, but needs to capture more than simple serial-chain kinematics. It also needs to reflect:
* ankle pitch and roll mapping through the parallel mechanism
* passive toe compliance and toe-ground interaction
* actuator saturation, friction, and delay
* contact geometry under the foot and toe
3\. Simulator rates and control rates [#3-simulator-rates-and-control-rates]
The training environment uses separate rates for physics, IO, and policy execution.
Here, `IO` means the observation and actuator-side update path, not policy inference itself. It is the loop that carries raw actuator-state timing, observation delay, and control-path bookkeeping before the policy consumes the resulting state.
| Layer | Rate | Role |
| --------------------- | ------ | ------------------------------------------------------------------------------------ |
| Physics step | 200 Hz | Integrates robot dynamics |
| Observation / IO path | 200 Hz | Updates actuator-state and actuator-side signals, including timing and delay effects |
| Policy step | 50 Hz | Produces commanded joint targets from the latest available IO state |
These separate rates matter because the policy is not trained on infinitely fresh simulator state. It is trained on data that already reflects timing artifacts in the control loop.
4\. Do not trust pristine simulator data [#4-do-not-trust-pristine-simulator-data]
A default simulator exposes perfectly synchronized state:
* all joint observations arrive at once
* sensor values are available without bus latency
* actuators respond at fixed timing
* projected gravity and orientation can be computed from ideal simulator state
This is useful for debugging, but it is not the data that real hardware provides. The locomotion stack therefore avoids building the policy around privileged measurements that do not exist on the robot.
5\. Processor-in-the-loop environment [#5-processor-in-the-loop-environment]
Asimov extends the simulation boundary beyond rigid-body dynamics by running the real firmware path inside the validation loop.
*Figure: Processor-in-the-loop architecture used for sim2real validation. The important point is that the control loop is closed through the real firmware path, with MuJoCo providing simulated IMU signals and the communication stack preserving the same software interfaces used on the robot.*
The processor-in-the-loop path includes:
* virtual CAN on Linux through the same SocketCAN software interface used by the actuator stack
* a MuJoCo bridge that reads `imu_ang_vel` and `imu_lin_acc`
* UDP transport of simulated IMU data into firmware
* the same FusionX path used on hardware to compute projected gravity for the policy
This design avoids a common failure mode where simulation uses a simplified software path that cannot exist on the real robot.
*Figure: CAN bus delay and jitter injection used to test timing robustness before hardware deployment. This figure is included here because locomotion transfer depended not only on rigid-body physics, but also on whether the simulated control path exposed the same latency variation and stale-data behavior seen on the real system.*
6\. Contact geometry and collision stability [#6-contact-geometry-and-collision-stability]
Foot-ground contact must be stable and interpretable. For this reason, the locomotion environment uses explicit collision primitives instead of relying on detailed mesh collision for learning.
Key choices include:
* capsule-based foot and toe contact geometry
* explicit toe and foot contact points
* contact tuning on foot and toe geometry
* conservative, repeatable contact behavior rather than maximum geometric fidelity
This reduces the risk that the policy learns from unstable collision artifacts.
7\. Hardware structure still constrains the simulator [#7-hardware-structure-still-constrains-the-simulator]
The simulation environment is specific to the Asimov leg design, not a generic humanoid simulator. The hardware constraints that shape the simulation — the parallel ankle mechanism, passive toe joints, actuator limits, and joint ranges — are documented in [Joint Design and Actuation](/asimov/0/hardware/joint-design-and-actuation) and [Deep Dive: System Identification](/guides/locomotion-training/reinforcement-learning-deep-dive-system-identification).
# Asimov Manager
Asimov Manager is the console that runs on the robot's Raspberry Pi 5 — think of it as the robot's router login page. You use it to set up, start, drive, update and troubleshoot Asimov 1 from a browser. There is no app to install and no cloud account.
Open it at `http://.local`, where `` is the hostname you set when flashing the Pi 5. The console serves plain HTTP on port 80. Until you set a password, the login is pre-filled, so the first sign-in is one click — see [Connect & Sign In](/asimov/1/operate/setup/connect).
In the console, **RPU** means the Robot Processing Unit in the torso, whose Radxa CM5 runs the motion-control firmware.
Pages [#pages]
| Group | Page | What it's for |
| ----------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| — | **Overview** | The robot's vital signs and its main action — see [Overview](#overview) |
| — | **Cockpit** | Drive the robot — see [Stand Up and Walk](/asimov/1/operate/drive/stand-up) |
| Robot | **Actuators** | Assign IDs, check and calibrate actuators — see [Calibrate Actuators](/asimov/1/operate/setup/calibrate-actuators) |
| Robot | **Firmware** | Confirm assembly, start and stop the firmware, Start on boot and the RPU link — see [First Start](/asimov/1/operate/setup/first-start) |
| Connections | **Wi-Fi** | Join and forget networks — see [Connect & Sign In](/asimov/1/operate/setup/connect#join-your-wi-fi) |
| Connections | **Controller** | Pair a Bluetooth gamepad — see [Gamepad](/asimov/1/operate/drive/gamepad) |
| System | **Updates** | Install a software update — see [Software Updates](/asimov/1/operate/software-updates) |
| System | **Developer** | Issue credentials for the SDK — see [Developer Credentials](#developer-credentials) |
| System | **Advanced Settings** | Every setting on the robot, raw. Prefer the dedicated pages where they exist |
| System | **Troubleshoot** | Health checks, service restarts and logs — see [Troubleshooting](/asimov/1/operate/troubleshooting) |
| User menu | **Account** | Password, appearance, version and settings reset — see [Account and Access](#account-and-access) |
On a phone, the sidebar becomes a menu behind **Open Menu** in the top bar.
The sidebar's device card shows the hostname, overall health, battery and any connected gamepad on every page. The E-Stop is in the page header of Overview, Firmware, Wi-Fi, Controller, Account and Troubleshoot on a desktop, and in the top bar of every page on a phone — see [Stopping the Robot](/asimov/1/operate/safety/stopping#e-stop).
Overview [#overview]
Overview has one main action, which depends on the robot's state:
| Firmware | Main action |
| ----------------------------------- | ---------------------------------------- |
| Stopped | **Start Robot** |
| Running | **Drive Robot**, plus **Turn Off Robot** |
| RPU unreachable or not controllable | None — fix the link first |
Its panels:
| Panel | What to look for |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Battery | A percentage, amber below 50% and red below 20%. A blank reading means Asimov Edge isn't running |
| Subsystem strip | **Firmware** (**Running**, **No RPU control** or **RPU unreachable**), **Controller** (the paired gamepad, or **Not connected**) and **Wi-Fi** (your network's name) |
| Actuator roster | Each joint's result from the last actuator check, such as **25 of 25 responding · checked** and when. **not current** means the result is old, not that anything failed |
| Software & services | The installed release, and **Video streaming** — whether the robot's video service (LiveKit) is running. The Cockpit doesn't show video in this release |
| Logs | The newest lines across services, with **View logs** for the full viewer |
The Setup Checklist [#the-setup-checklist]
Until the robot is fully set up, the sidebar shows a **Finish setting up** checklist. [The Setup Checklist](/asimov/1/operate/setup#the-setup-checklist) explains each row.
Account and Access [#account-and-access]
Password [#password]
Change it on **Account**, from the user menu — see [Set a Password](/asimov/1/operate/setup/connect#set-a-password).
Forgotten Password [#forgotten-password]
Asimov Manager has no reset link. Over [SSH](#command-line), clear the stored password:
```bash
sudo asimovctl config reset manager.password_hash
```
The login goes back to the pre-filled default. Sign in and set a new password straight away.
Reset All Settings [#reset-all-settings]
**Reset All Settings**, under Advanced on the Account page, returns the robot's RPU, video (LiveKit) and Asimov Edge settings to factory defaults. Asimov Edge's parameters, including the robot's serial, go back to their defaults too.
It keeps your login, saved Wi-Fi networks, SDK credentials, the assembly confirmation, and actuator IDs and calibration.
{/* TODO(manager): the confirmation says "the robot's identity is kept", but the reset restores asimov_edge.params, including serial = "MENLO-0001". Fix the copy or the scope, then update this section. */}
Developer Credentials [#developer-credentials]
A developer credential lets a computer running the [Python SDK](/sdk/connection-modes) join the robot's LiveKit room. You need one to connect the SDK in its **hybrid** or **livekit** connection mode, which is also how the SDK sees the camera and hears the microphone. The Cockpit needs no credential, and neither does the SDK's **udp** mode.
The robot runs its own LiveKit server and is set up to use it from the first boot, so there is nothing to configure before you issue a credential.
**Issue a Credential**
Open **Developer** in the sidebar. Under **Issue a credential**, name the computer that will use it, choose **Observe** or **Control**, and select **Issue credential**. The credential is shown once, so copy it then; if you lose it, revoke it under **Issued credentials** and issue another. [Drive from Python](/asimov/1/operate/drive/python-sdk#get-an-sdk-credential) walks through the page step by step and connects the SDK with `menlo setup`.
**Observe and Control**
| Role | What it allows in the LiveKit room |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Observe** (default) | Watch the camera and listen to the microphone. The LiveKit server refuses its commands, whatever program holds it |
| **Control** | Everything Observe allows, plus sending commands and talking through the speaker. Commands still pass through the robot's safety layer, and the Cockpit keeps priority |
The role limits driving only in the **livekit** mode. In **hybrid**, commands travel over UDP on the robot's network, which has no sign-in, so an Observe credential still gets the camera and microphone and the SDK can still drive. Hybrid also needs the robot to accept UDP commands and send state to your computer; see [Hybrid](/sdk/connection-modes#hybrid).
**Revoke a Credential**
Under **Issued credentials**, select **Revoke** next to the credential and confirm. The computer holding it can no longer connect. A session that is already running keeps working until its current LiveKit token expires, at most 12 hours.
**Reset All Settings** keeps issued credentials.
**The LiveKit Secret Stays on the Robot**
The credential is not the LiveKit key or secret. Each time the SDK connects, it trades the credential for a short-lived LiveKit token from Asimov Manager, so the key and secret never leave the robot and you never need them.
To use a LiveKit server other than the robot's own, set **LiveKit URL**, **LiveKit key** and **LiveKit secret** under **Asimov Edge** in **Advanced Settings**, and select **Save & Restart Asimov Edge**. Saving checks that the server accepts the key and secret first. If the **LiveKit URL** row on the Developer page reads **not set**, the robot publishes no video or audio until you set it here.
Command Line [#command-line]
Everything the console does is also available over SSH on the Pi 5. Sign in with the username and password you set in Raspberry Pi Imager when [flashing it](/asimov/1/build/assembly-preparations/software-setup/flash-raspberry-pi), then use `asimovctl` with `sudo`:
```bash
asimovctl status # services, RPU link and setup state
asimovctl start|stop|restart # asimov-edge, asimov-firmware or livekit-server
asimovctl enable|disable # start on boot
asimovctl config show # every setting, secrets masked
asimovctl config set
asimovctl config reset
asimovctl apply # write settings to the services
asimovctl reset-setup --yes # undo Confirm assembly; stops the firmware
asimovctl update status # the current software update
```
The browser and the command line control the same services, so anything you start from one you can stop from the other.
What It Manages [#what-it-manages]
Asimov Manager and Asimov Edge run on the Pi 5. Asimov Edge carries commands and telemetry between the console and the RPU, whose Radxa CM5 runs the motion-control firmware. The Pi 5 and RPU share a private Ethernet link, which the console reports as two checks: **Network** (the RPU answers on the wire) and **Control** (the console can manage it). An unplugged cable and a stopped service never look the same.
What It Doesn't Do [#what-it-doesnt-do]
* **Run the control loop.** The firmware on the CM5 does; the console starts it, stops it and relays your drive input.
* **Command actuators while the firmware runs.** The Actuators page works only with the firmware stopped.
* **Show video.** The Cockpit's camera panel reads **No video** in this release.
* **Restart or shut down the robot's computers.**
* **Manage more than one robot.** There is no fleet view.
* **Flash boards.** It is installed and updated as part of the robot's software.
# Power On and Off
A session with a commissioned robot runs: power on, start the robot, [drive](/asimov/1/operate/drive/stand-up), turn the robot off, power off.
Power On [#power-on]
. Support the robot on its gantry hook or a bench.
. Power it on at the battery unit.
. Wait about two minutes. The Raspberry Pi 5 joins a saved Wi-Fi network, or starts its setup hotspot if it can't, and its link to the Robot Processing Unit (RPU) comes up.
. Open `http://.local`.
{/* TODO(hardware): confirm the power-on control on an assembled robot (the battery unit's button?) and add a photo. */}
The firmware starts by itself only if its **Start on boot** switch is on, which it isn't by default. Otherwise the actuators stay unpowered and the robot stays limp until you start it.
Start the Robot [#start-the-robot]
. Check that the robot is supported and everyone is clear of it.
. On **Overview**, select **Start Robot** and confirm.
Start Robot starts Asimov Edge if it isn't running, then the firmware. The actuators come on in Damp: powered, but limp.
If **Start Robot** is greyed out, its tooltip says why — most often the RPU is unreachable, or assembly was never confirmed (confirm it on the **Firmware** page, as in [First Start](/asimov/1/operate/setup/first-start#confirm-assembly)). While an actuator check is running, the button reads **Waiting for actuator check…**.
Once the firmware is running, Overview's main action becomes **Drive Robot**. Before you drive, glance at Overview: a battery percentage, **Running** for the firmware, and **25 of 25 responding**. [Overview](/asimov/1/operate/asimov-manager#overview) explains every panel.
Turn the Robot Off [#turn-the-robot-off]
. If the robot is walking, bring it back down first — see [Bring It Back Down](/asimov/1/operate/drive/stand-up#bring-it-back-down).
. Make sure it is supported.
. On **Overview**, select **Turn Off Robot** and confirm.
This stops the firmware and nothing else. All 25 actuators go offline and driving stops; Asimov Edge and the console stay up, so the robot is still reachable. **Stop Firmware** on the Firmware page does the same, and works whenever the firmware is running.
Power Off [#power-off]
. Turn the robot off, as above, with it seated or hanging from its gantry hook.
. Press the button on the battery unit.
Asimov Manager can't restart or shut down the robot's computers.
{/* TODO(hardware): confirm whether the Pi 5 needs a clean shutdown before power is removed. */}
Start on Boot [#start-on-boot]
The **Start on boot** switch on the Firmware page starts the firmware automatically whenever the robot powers on. It is off by default, and available once assembly is confirmed.
Leave it off unless the robot lives somewhere it is always safely supported. With it on, the actuators power up after every power cut, with nobody there to check.
Battery and Charging [#battery-and-charging]
The battery level shows in the sidebar's device card, the phone top bar, Overview and the Cockpit header. It turns amber below 50% and red below 20%. The Cockpit's Telemetry tab has the detail: charge, voltage, current, maximum cell temperature and protection flags.
Asimov Manager doesn't warn you or act on a low battery, so keep an eye on the level.
{/* TODO(hardware): charging procedure — connector, whether it can charge while running, storage charge level, runtime, and what the robot does at low battery (Asimov Edge drives a battery-warning buzzer; threshold unknown). */}
# Software Updates
Asimov 1 updates as one unit: a single signed file updates the robot's apps and the Radxa CM5 firmware for the Robot Processing Unit (RPU) together. You install it from the **Updates** page in Asimov Manager, with no terminal needed.
Get the Update File [#get-the-update-file]
Updates come as a signed `.mender` file of up to 2 GB. Fetching updates online isn't available yet — **Check Now** is disabled — so add the file by hand.
{/* TODO: say where owners download the .mender update file. */}
Install an Update [#install-an-update]
. Support the robot and [turn it off](/asimov/1/operate/power#turn-the-robot-off). The update restarts the RPU.
. Open **Updates**, select **Choose File…** and pick the `.mender` file.
. Start the update. You can leave the page and come back — the progress card tracks the update either way.
An update isn't finished when it installs: the robot runs a health check on its services first, then reports the result.
| Result | Meaning |
| ---------------------------------------- | --------------------------------------------------------------------------------- |
| **Updated to Robot Software \** | Done. The services stayed healthy for the whole check |
| **Update rolled back** | Something failed the health check, and the robot returned to the previous version |
| **Update undone** or **Update closed** | The update was reversed or abandoned before it finished |
| **Update failed** | It didn't install. The log on the page says why |
Select **OK** to dismiss the result. If the page instead says an update was interrupted — for example **The robot restarted during an update** — select **Finish Update** to run the health check again.
Messages such as **The controller kept the new version** point to recovery steps that are run over SSH. If you see one without having done anything unusual, note the exact message and contact support rather than improvising.
Check Versions [#check-versions]
* **Updates → Installed** lists **Robot Apps** and **Controller (RPU)** — the CM5 firmware.
* Overview's **Software** row shows the installed release.
* **Account** shows the console's own version, which matches the release.
Updates don't change actuator IDs, calibration or the assembly confirmation.
# Troubleshooting
Start from the symptom. Most fixes involve the **Troubleshoot** page, which shows the robot's health checks — the Robot Processing Unit (RPU) link, the video service and the Bluetooth radio — with a **Restart** button for each service. **Check again** runs every check again.
**Restart** on the **Firmware (RPU)** row restarts the firmware immediately, which takes the actuators offline. Support the robot before using it.
The Console Will Not Load [#the-console-will-not-load]
| Symptom | What to do |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `http://.local` doesn't answer | Allow two minutes from power-on — longer if the robot falls back to its hotspot — and check that your device is on the same network as the robot |
| `.local` doesn't resolve | Common on Android. On the setup hotspot, use `http://10.42.0.1`. On your own network, use the address your router assigned the robot |
| It loads on the hotspot but not on your Wi-Fi | Some routers, especially guest networks, stop devices from reaching each other. Use your main network |
| The robot never joins a 5 GHz network | Try a 2.4 GHz network, or a 5 GHz channel from 36 to 48 |
| The setup hotspot never appears | It starts only when the robot has nothing to join: at power-on, after a failed join, or after you forget its last network |
| The robot dropped off your Wi-Fi | The hotspot doesn't return on its own once the robot has booted. Power-cycle the robot so it rejoins or starts the hotspot |
The RPU Is Unreachable [#the-rpu-is-unreachable]
Nothing that moves the robot works until this link is healthy. The **Firmware** page splits it into two checks:
| Failing check | Meaning | What to do |
| ------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Network** | The RPU isn't answering on the wire | Check the Ethernet cable between the Raspberry Pi 5 and the Radxa Carrier Board , and power to the Head Compute Unit and RPU. Power down before reseating connections |
| **Control** | The RPU answers, but the console can't manage it | The RPU is still booting or its services are down. Wait a minute and reload the page; if it persists, power-cycle the robot |
While the RPU is unreachable, the setup checklist greys out the rows that depend on it ("Waiting on the RPU"), and the Actuators and Firmware pages lock their tools. That is expected, not a second fault.
The Firmware Will Not Start [#the-firmware-will-not-start]
| Symptom | What to do |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| **Start Robot** or **Start Firmware** is greyed out | Read its tooltip, which names what's missing |
| Assembly was never confirmed | Confirm it on the **Firmware** page — see [First Start](/asimov/1/operate/setup/first-start#confirm-assembly) |
| **Waiting for actuator check…** | An actuator check is using the CAN buses for about 15 seconds. Wait, then try again |
| The RPU is unreachable | Fix the link first — see above |
The Actuators Page Is Locked [#the-actuators-page-is-locked]
Its tools work only with the firmware stopped and the RPU reachable. The lock pill names the cause — for example **Stop the firmware to change actuators**. Follow its **Open Firmware ›** link, fix the cause, then select **Retry**.
The Robot Will Not Move [#the-robot-will-not-move]
See [If You Cannot Drive](/asimov/1/operate/drive/cockpit#if-you-cannot-drive). The usual causes: no session, the robot isn't in Walk, Asimov Edge isn't running, or a gamepad has control.
The Robot Stopped or Fell on Its Own [#the-robot-stopped-or-fell-on-its-own]
The firmware damps the robot when it detects a fault. Look for **fault N** on the Cockpit's status line and for **Error flags** in its Telemetry tab, then check the **Firmware** log for that moment. An overheating actuator and a lost command stream look the same from outside, and completely different in the log.
To get going again, follow [Recovering After an E-Stop or a Fall](/asimov/1/operate/safety/stopping#recovering-after-an-e-stop-or-a-fall).
The Robot Drifts, Stumbles or Leans [#the-robot-drifts-stumbles-or-leans]
Suspect a zero point, especially after a repair — see [Recalibration After Repairs](/asimov/1/operate/setup/calibrate-actuators#recalibration-after-repairs). Rule out wiring and actuator faults first.
You Forgot the Password [#you-forgot-the-password]
See [Forgotten Password](/asimov/1/operate/asimov-manager#forgotten-password).
Reading the Logs [#reading-the-logs]
The **Logs** viewer on the Troubleshoot page shows five sources — **Edge**, **LiveKit**, **RPi kernel**, **Firmware** and **RPU kernel** — newest first, updating live. Scroll down for older entries.
**RPi kernel** is the Pi 5's kernel log; **RPU kernel** is the kernel log from the Radxa CM5 in the RPU.
Asimov Manager keeps the newest 4,000 entries per source. Older history survives a restart only if persistent logging is enabled on that computer. There is no export, so when you report a problem, copy the relevant lines or take a screenshot while they are still there.
The Cockpit's Logs tab shows the Edge and Firmware logs without leaving the Cockpit, and Overview's Logs panel shows the newest lines across services.
# Overview
Asimov 1 represents a more open approach to humanoid robotics, giving researchers and developers the freedom to understand, repair, customize, and develop the robot across its full stack.
| Specification | Asimov 1 |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Height | Approximately 1.2 m (3.94 ft) |
| Weight | Approximately 35 kg (77 lb) |
| Powered joints | 25 |
| Legs | 6 powered joints per leg |
| Arms | 5 powered joints per arm |
| Body and neck | 1 waist joint and 2 neck joints |
| Compute | Radxa CM5 for motion control; Raspberry Pi 5 for media and networking |
| Sensing and media | IMU, monocular camera, stereo microphone array, and speaker |
| Power | Rechargeable 13S4P lithium-ion battery system |
The Compute Module consists of two connected stacks, the **Head Compute Unit** and the **Robot Processing Unit (RPU)**:
* The Head Compute Unit contains the Pi 5 , Head Board , and Media HAT Board . The Media HAT Board provides the buzzer and brownout protection.
* The RPU contains the Radxa Carrier Board , CM5 , and Power Distribution Board (PDB) . The Carrier Board with the CM5 mounted forms the **Motion Control Board (MCB)**; the bare Radxa Carrier Board is one component, not the complete MCB.
Key Features [#key-features]
A Fully Open Platform [#a-fully-open-platform]
Asimov 1 makes its mechanical design, electrical design, simulation models, firmware, and bill of materials available directly to builders.
View on GitHub ↗
Coming soon
Bill of Materials [#bill-of-materials]
Enter your email below to receive the Asimov 1 Bill of Materials.
Software Availability [#software-availability]
| Capability | Status |
| -------------------------------- | ----------------------------------------------- |
| Full-body locomotion policy | Available in the supplied firmware |
| Directional remote teleoperation | Available |
| Direct actuator control | Available through the Asimov Edge or Asimov SDK |
| Robot telemetry | Available at 10 Hz |
| MuJoCo and URDF models | Provided with the Asimov 1 files |
| Full-body teleoperation | In development |
| Asimov Client SDK | In development |
| Menlo Platform | Coming soon |
***
# Tools & Supplies
These are the tools you'll need to assemble an Asimov 1, along with some optional extras. Use this guide to prepare your workbench, see what each tool is for, and find the products we use ourselves.
The linked products are references, not a requirement to buy a particular brand. None of these links are affiliate links or advertisements; they're simply what we use, so feel free to choose suitable tools from your favourite brand instead.
Required Tools and Supplies [#required-tools-and-supplies]
These tools and supplies are needed for standard assembly, including the wire joins required at certain actuator joints and basic electrical checks.
Used for most of the robot's assembly. Make sure you have 2, 2.5, 3, 4, 5, and 6 mm keys available.
As an Asimov customer, you do not need to buy this.
A metric Allen-key set is included free with your Asimov order or preorder and arrives with your robot delivery.
Used to secure the battery switch to the battery casing. Choose a Phillips bit that fits the screw head correctly.
Used to trim wire ends or small parts where an assembly step calls for cutting. Check the cutter's material and wire-size rating before use.
Used to remove insulation from wires before connecting actuators at certain joints.
Used to shrink heat-shrink tubing over completed wire joins. Control the heat and keep it away from nearby plastics and other heat-sensitive parts.
Used with solder to join wire ends at the joint locations identified in the assembly instructions. Use a suitable tip and temperature for the wire and solder.
Holds wire ends steady while you solder them together. Position the clamps so they do not damage the insulation or strain the wire.
Needed for continuity checks after making wire connections to confirm the intended electrical paths are connected. Also used for voltage checks when debugging the electrical system. Perform continuity checks only with the battery disconnected and the circuit unpowered.
Used with the soldering iron to make the documented wire joins. Lead-free solder still requires suitable fume control.
Insulates soldered wire joins. Select tubing that fits over the join and shrinks to a secure fit; slide it onto the wire before soldering when the join would prevent fitting it afterward.
Helps prevent specified bolts from loosening after assembly. Apply to the threads as directed before tightening; do not substitute a high-strength grade or apply it to every fastener indiscriminately.
Secures wires to metal at specified routing locations to help prevent pinching. Check material compatibility and keep adhesive away from connectors and moving joints.
Used for temporary labels, notes, and holding parts, wires, nuts, or bolts in place during assembly. It is not a substitute for electrical insulation or permanent wire retention.
Optional Tools and Workshop Equipment [#optional-tools-and-workshop-equipment]
These items can make assembly or troubleshooting easier, but you do not need to buy them for standard assembly. This section also includes optional teleoperation equipment and development and prototyping equipment that we use in our workshop; customers do not need any of it to assemble the robot.
Our open-source crane for lifting and positioning the robot. Build guide, BOM, and design files are on GitHub.
Our open reference design for a sturdy support stool to sit on or position the robot during setup and maintenance.
We will provide the GitHub for this soon.
Can help gently seat a tight-fitting part where the assembly instructions permit it. Check alignment and obstructions first; do not force a defective part or strike an actuator, bearing, or electronics.
Speeds up repetitive tightening and loosening instead of turning every fastener manually. Start threads by hand and use the final-tightening method specified in the assembly step to avoid cross-threading or over-tightening.
Warning: a cordless screwdriver increases the risk of stripping bolt heads or threads.
Avoid using one unless you are experienced with powered screwdrivers and torque control. Use hand tools if you are unsure.
Used to visually locate unusually warm areas when investigating possible overheating. It does not replace the robot's temperature monitoring or safety checks.
Helps capture fumes near the soldering work area. This is the desktop unit we use; an appropriate existing extraction setup may serve instead.
Follow the manufacturer's positioning and filter-maintenance instructions, and provide suitable ventilation.
Used with the PICO Motion Tracker for teleoperation and control of Asimov. This headset is optional and is not required to assemble the robot.
Used with the PICO 4 headset for teleoperation and control of Asimov. This tracker is optional and is not required to assemble the robot.
Used for PCB heating during electronics development and prototyping.
A workshop tool for fabrication and prototyping.
Used to machine parts for development and prototyping.
Used to inspect electrical signals during advanced electronics debugging and prototyping.
Used to make prototype parts and iterate on designs.
Used in our workshop for battery inspection and advanced electrical diagnostics.
Capabilities vary across the linked series. Battery DC-current measurements require a DC-capable model.
# Python SDK
`menlo-sdk` drives the robot from Python: stand, balance, walk, joints, state, camera and
audio, over the robot's network or its LiveKit room. It is a client of Asimov Edge, so every
command passes through the robot's safety layer, and the Cockpit and a paired gamepad
outrank it.
Two guides cover it. [Drive from Python](/asimov/1/operate/drive/python-sdk) is the hands-on
path: the robot's settings, a credential, install, and a first walk with video and sound.
The [Python SDK](/sdk) section is the depth: every connection mode, verb, error and example.
For a first run, hang the robot from its gantry hook with both feet on the floor, and give a
walk 2 m of clear floor ahead. Nothing in the SDK is an emergency stop: keep the
E-Stop in Asimov Manager open, or be ready to cut power at the battery unit. Read
[Safety](/sdk/safety) before the first run.
Install [#install]
Python 3.12 or newer.
```bash
pip install menlo-sdk
menlo setup
```
`menlo setup` asks for a connection mode and what it needs, checks them against the robot,
and saves the robot, so every script and command below finds it without arguments. The
`udp` mode needs the robot to send UDP state to your computer; `hybrid` and `livekit` need an
SDK credential from the Developer page in Asimov Manager
([Drive from Python](/asimov/1/operate/drive/python-sdk#get-an-sdk-credential)).
Run the Examples [#run-the-examples]
```bash
git clone https://github.com/menloresearch/menlo-sdk.git
cd menlo-sdk/examples
```
. `python check.py` reports what the robot reports: robot mode, arming, faults, alerts,
the hottest joint and the battery. It sends nothing.
. `python stand.py` stands the robot up and returns once it is armed. Hang the robot from
its gantry hook with both feet on the floor first: STAND has no balance loop.
. `python balance.py` puts the robot in MOVE at zero velocity, where the walking policy
balances it. Slacken the gantry only after it returns.
. `python walk.py` walks forward at 0.3 m/s for 3 s, then balances in place. Give the robot
2 m of clear floor ahead.
. `python rest.py` brings the robot to rest: MOVE, then STAND, then DAMP. It asks first
whether the robot is hanging from its gantry hook or seated on a stool or bench. `python
damp.py` puts the robot in DAMP directly: every actuator stops holding its position, so a
standing robot falls. Hang it from its gantry hook or seat it on a stool or bench first.
. `python keyboard.py` drives from the keyboard: `w`/`s` walk, `a`/`d` strafe, `q`/`e`
turn, space balances in place, `t` stands (asks first in MOVE), `b` damps after asking,
`x` quits. It walks the robot backward and sideways too: keep clear floor all around it.
The SDK refuses a command only when there is no live state; the firmware decides the rest.
Every script that moves the robot calls `guard(robot)` from `guard.py` first, an optional
guard of your own that stops the script on a fault, an alert, a hot actuator or a low
battery. Edit its limits, or delete the line. The rest of the scripts, including the camera, microphone and speaker, are in
[Examples](/sdk/examples); the sequence and why it is ordered this way are in
[Standing and Balancing](/sdk/safety#standing-and-balancing).
Operate from the Command Line [#operate-from-the-command-line]
The same verbs, without a script:
```bash
menlo status
menlo stand
menlo balance
menlo walk --vx 0.3 --duration 3
menlo damp
```
`menlo status` sends nothing. `menlo stand`, `menlo balance`, `menlo walk` and `menlo damp`
show the robot's facts and ask `Proceed? [y/N]` before they send, unless you pass `-y`.
`stand`, `balance` and `walk` refuse only when there is no live state. See
[Command Line](/sdk/cli).
Learn More [#learn-more]
* [Quickstart](/sdk/quickstart): the check, stand, balance, walk and damp scripts, line by line
* [Move the Robot](/sdk/move): velocity, holding it, limits, ending a walk
* [Connection Modes](/sdk/connection-modes): `udp`, `hybrid` and `livekit`
* [SDK Reference](/sdk/reference): every class, verb and error
# Resources
This section will collect further reading, community and support links, videos, and other useful Asimov 1 material. Content is TBC.
# Train Asimov 1
Guidance for deploying a trained policy onto Asimov 1 — simulation-to-hardware transfer, emulator testing, and on-robot rollout — is coming soon. This page is TBC.
Training Tutorials [#training-tutorials]
The locomotion training tutorials live in the [Training](/guides/locomotion-training) section:
* [Understanding your simulation environment](/guides/locomotion-training/understanding-your-simulation-environment)
* [Reinforcement learning for locomotion](/guides/locomotion-training/reinforcement-learning-for-locomotion)
* [Simulation training environment](/guides/locomotion-training/reinforcement-learning-simulation-training-environment)
* [Reward design](/guides/locomotion-training/reinforcement-learning-reward-design)
* [Domain randomization](/guides/locomotion-training/reinforcement-learning-domain-randomization)
* [System identification](/guides/locomotion-training/reinforcement-learning-deep-dive-system-identification)
* [Policy deployment](/guides/locomotion-training/reinforcement-learning-policy-deployment)
# 3D Printing and Fabrication
This chapter organizes the mechanical build around two fabrication paths:
* parts that can be produced with additive manufacturing
* parts that should be outsourced because they require higher strength, tighter tolerances, or a different process
1\. Part Naming Convention [#1-part-naming-convention]
We named the CAD files so you can quickly identify the required material for
each part.
Each part name includes a fabrication-code letter indicating its intended
manufacturing method and material family:
*Figure: Naming convention for the part manufactorying*
* `A` parts are not standard 3D printed parts. If a part is labeled as Aluminum 7075, it should be CNC machined.
* `B` parts are 3D printable in 316L stainless steel, but only with a metal printing process that supports SLM 316L.
* `C` parts are 3D printable in PA12 nylon, but only with a polymer printing process that supports PA12, such as SLS or MJF.
* `X` parts are off-the-shelf components and should be purchased directly rather than fabricated.
NOTE:
. The fabrication method must match the material code in the part name.
. Making parts with FDM 3D printing will not guarantee the tolerances and required strength for the system.
You can also refer to [Mechanical Design Frame](/asimov/0/hardware/frame-and-structural-components) to identify which module each part belongs to by mapping part indices to the module part lists.
After gathering all the parts, you can [start preparing the tools and finally start assembly](/asimov/0/assembly-manual)!
# Frame and Structural Components
This chapter describes how the Asimov humanoid frame is organized, how major structural modules connect, and why the structural design choices were made.
*
Download CAD
file
You can view the STEP source file on GitHub and download it from there.
1\. Asimov Humanoid Structural Architecture [#1-asimov-humanoid-structural-architecture]
The Asimov 0 legs are organized from pelvis to
the ground, with 12 active degrees of freedom and 2 passive toe joints.
Each leg is divided into pelvis, hip, knee, and foot modules, with 6
active DOF per leg: hip pitch, yaw, and roll; knee pitch; and ankle pitch and
roll.
2\. Structural Breakdown of Main Modules [#2-structural-breakdown-of-main-modules]
This exploded view of the Asimov 0 legs shows all parts included in the CAD
files for manufacturing. Components are labeled with indices 1-79, so you can use this
diagram as a checklist to confirm you have all CAD-modeled parts. Fasteners and other
standard hardware (for example, screws, nuts, and washers) are not included.
*Figure: Exploded view of the legs with part index labeled.*
We separate the robot this into 4 modules: Pelvis, Hip, Knee, and Feet, aligning to the assembly unit.
For each section, we labaled the parts included according to their material. Also for each part we include the indexs labeled in the exploded view for a quick check.
Pelvis Module [#pelvis-module]
Hip Module [#hip-module]
Knee Module [#knee-module]
Ankle/Foot Module [#anklefoot-module]
3\. Joint Structural Packaging [#3-joint-structural-packaging]
Joint Coordinate [#joint-coordinate]
The picture below shows the joint rotation axis with 0 joint position. The frame convention is blue for z, red for x, and green for y.
Joint Index and Joint rotation range [#joint-index-and-joint-rotation-range]
| Joint Index | Joint Name | Motion Range (rad) | Axis (xyz) |
| ----------- | -------------------------- | ------------------ | -------------------- |
| 1 | left\_hip\_pitch\_joint | -2.09 \~ 1.00 | 0 0.70711 -0.70711 |
| 2 | left\_hip\_roll\_joint | -0.79 \~ 0.79 | 1 0 0 |
| 3 | left\_hip\_yaw\_joint | -0.79 \~ 0.79 | 0 0 -1 |
| 4 | left\_knee\_joint | 0.00 \~ 1.50 | 0 1 0 |
| 5 | left\_ankle\_pitch\_joint | -0.50 \~ 0.50 | 0 1 0 |
| 6 | left\_ankle\_roll\_joint | -0.35 \~ 0.35 | -1 0 0 |
| 7 | left\_toe\_joint | -0.60 \~ 0.00 | 0.010532 0.99994 0 |
| 8 | right\_hip\_pitch\_joint | -1.00 \~ 2.09 | 0 -0.70711 -0.70711 |
| 9 | right\_hip\_roll\_joint | -0.79 \~ 0.79 | 1 0 0 |
| 10 | right\_hip\_yaw\_joint | -0.79 \~ 0.79 | 0 0 -1 |
| 11 | right\_knee\_joint | -1.50 \~ 0.00 | 0 -1 0 |
| 12 | right\_ankle\_pitch\_joint | -0.50 \~ 0.50 | 0 -1 0 |
| 13 | right\_ankle\_roll\_joint | -0.35 \~ 0.35 | -1 0 0 |
| 14 | right\_toe\_joint | 0.00 \~ 0.60 | 0.0074685 -0.99997 0 |
If you want to know more details about the joints, welcome to the next chapter [Joint Design and Actuation](/asimov/0/hardware/joint-design-and-actuation), where we covered the actuators, ankle and toe joints design.
# Hardware Design
This section documents the hardware structure of Asimov's v0 legs, focusing on the frame, joints, fabrication workflow, and actuator-related hardware.
The chapters in this section are organized as follows:
* [Frame and Structural Components](/asimov/0/hardware/frame-and-structural-components)
* [Joint Design and Actuation](/asimov/0/hardware/joint-design-and-actuation)
* [3D Printing and Fabrication](/asimov/0/hardware/3d-printing-and-fabrication)
After reviewing the hardware chapters, continue to the [Assembly Manual](/asimov/0/assembly-manual) to prepare parts, tools, and bring-up steps.
# Joint Design and Actuation
This chapter documents joint-level actuation limits and the ankle mechanism model used for design and control.
1\. Joint Actuator Specification [#1-joint-actuator-specification]
The table below maps the leg joints to their actuator entries and limit values. Torque and velocity limits are from the URDF joint limits. Passive toe joints are included for completeness and are marked with `N/A` for actuator-specific fields.
| Actuator ID | Joint Name | Motion Range (rad) | Effort Limit (Nm) | Velocity Limit (rad/s) | Kt |
| ------------------ | -------------------------- | ------------------ | ----------------- | ---------------------- | ---- |
| EC-A6416-P2-25 | left\_hip\_pitch\_joint | -2.09 \~ 1.00 | 120.00 | 12.57 | 2.75 |
| EC-A5013-H17-100 | left\_hip\_roll\_joint | -0.79 \~ 0.79 | 90.00 | 3.98 | 7.15 |
| EC-A3814-H14-107 | left\_hip\_yaw\_joint | -0.79 \~ 0.79 | 60.00 | 5.45 | 5.70 |
| EC-A4315-P2-36 | left\_knee\_joint | 0.00 \~ 1.50 | 75.00 | 12.25 | 2.53 |
| EC-A4310-P2-36 | left\_ankle\_pitch\_joint | -0.50 \~ 0.50 | 36.00 | 9.32 | 1.81 |
| EC-A4310-P2-36 | left\_ankle\_roll\_joint | -0.35 \~ 0.35 | 36.00 | 9.32 | 1.81 |
| N/A(passive joint) | left\_toe\_joint | -0.60 \~ 0.00 | N/A | N/A | N/A |
| EC-A6416-P2-25 | right\_hip\_pitch\_joint | -1.00 \~ 2.09 | 120.00 | 12.57 | 2.75 |
| EC-A5013-H17-100 | right\_hip\_roll\_joint | -0.79 \~ 0.79 | 90.00 | 3.98 | 7.15 |
| EC-A3814-H14-107 | right\_hip\_yaw\_joint | -0.79 \~ 0.79 | 60.00 | 5.45 | 5.70 |
| EC-A4315-P2-36 | right\_knee\_joint | -1.50 \~ 0.00 | 75.00 | 12.25 | 2.53 |
| EC-A4310-P2-36 | right\_ankle\_pitch\_joint | -0.50 \~ 0.50 | 36.00 | 9.32 | 1.81 |
| EC-A4310-P2-36 | right\_ankle\_roll\_joint | -0.35 \~ 0.35 | 36.00 | 9.32 | 1.81 |
| N/A(passive joint) | right\_toe\_joint | 0.00 \~ 0.60 | N/A | N/A | N/A |
2\. Ankle Parallel Mechanism [#2-ankle-parallel-mechanism]
Parallel Mechanical Design [#parallel-mechanical-design]
*Figure: RSU ankle parallel mechanism*
Asimov uses a parallel RSU ankle (Revolute-Spherical-Universal) instead of a simple serial single-DOF ankle. The RSU layout provides two ankle DOFs (pitch and roll) while allowing coupled actuation through two rotary actuators.
Kinematic Mapping for the Parallel Mechanism [#kinematic-mapping-for-the-parallel-mechanism]
*Figure: Ankle joint overview*
In simulation, we model the ankle pitch and roll joints as directly actuated at their ideal joint
locations to avoid the computational cost of simulating the full parallel mechanism. In hardware,
pitch and roll are controlled by moving two endpoints on a straight bar; these endpoints are
connected through parallel linkages to ankle actuators A and B. Because the ankle pitch and roll
joints are not directly actuated in the real system, kinematic mapping is required for sim-to-real
policy deployment.
We define the following variables:
* `theta_p`: ankle pitch angle (joint space)
* `theta_r`: ankle roll angle (joint space)
* `theta_A`: actuator A rotation angle
* `theta_B`: actuator B rotation angle
* `r_A`, `r_B`: effective crank/linkage radii of actuators A and B
* `r`: common radius when `r_A = r_B = r`
* `d`: distance from the ankle pivot to the bar midpoint line
* `c`: distance between the left and right bar endpoints
Assumptions:
* Small-angle approximation is used: `sin(theta) ≈ theta`, `tan(theta) ≈ theta` (angles in radians)
* The bar/linkage geometry is rigid and symmetric
* `r_A` and `r_B` are constant effective radii (no compliance, backlash, or slip)
* Sign convention is fixed and consistent:
* `+y` is away from the ground, `-y` is toward the ground
* Positive actuator/joint rotation directions are predefined and unchanged
* The same derivation frame (right-leg convention) is used when applying the equations
* For simplified forms, assume `r_A = r_B = r`
Mapping equations (`AB -> PR`):
`theta_p = (r_A * theta_A - r_B * theta_B) / (2 * d)`
`theta_p = (theta_A - theta_B) * (r / (2 * d))`
`theta_r = - (1 / c) * (r_A * theta_A + r_B * theta_B)`
`theta_r = - (theta_A + theta_B) * (r / c)`
Code example:
```c
float right_pitch = (right_A - right_B) / (2.0f * K_PITCH);
float right_roll = -(right_A + right_B) / (2.0f * K_ROLL);
float left_pitch = (left_A - left_B) / (2.0f * K_PITCH);
float left_roll = -(left_A + left_B) / (2.0f * K_ROLL);
```
For more information on deriving the ankle mechanism equations, see the [ankle mechanism derivation note](https://github.com/asimovinc/asimov-v0/blob/main/mechanical/ankle_mechanism.md).
3\. Passive toe joints [#3-passive-toe-joints]
*Figure: Passive toe joint design*
Similar to how human toes passively bend to conform to the ground during stance, an articulated toe increases effective contact area and helps distribute load more evenly. The main benefit appears during the stance-to-push-off transition.
As the body moves forward, the toe rocker allows the foot to roll rather than pivot on a rigid edge. This reduces peak local forces, maintains ground contact, and increases effective contact area, improving traction stability and forward propulsion.
In Asimov, this is implemented as an additional toe hinge that is articulated but not actively actuated. The toe joint is passive and compliant, using elastic/spring-like return behavior instead of a dedicated actuator. This approach captures most toe-rollover benefits while avoiding the added complexity, mass, and control burden of another powered joint.
4\. References [#4-references]
. [https://arxiv.org/pdf/2509.16469](https://arxiv.org/pdf/2509.16469)
. Unitree G1 developer documentation (parallel-ankle context): [https://support.unitree.com/home/en/G1\_developer/basic\_motion\_routine](https://support.unitree.com/home/en/G1_developer/basic_motion_routine)
After the previous chapters, you should now have a clear understanding of our mechanical design. You can now begin manufacturing the parts to build your robot in [3D Printing and Fabrication](/asimov/0/hardware/3d-printing-and-fabrication).
# Extend Asimov 1
Validated guidance for hands, end effectors, sensors, additional compute, custom covers, head redesigns, mounting, power, communications, and software integration is coming soon. This section is TBC.
# Calibrate Actuators
Check that all 25 actuators answer without faults, then record each one's zero position at the robot's home pose. Everything happens on the **Actuators** page of Asimov Manager.
Support the robot and its limbs so nothing can fall or sag, keep people clear of the joints, and know how to [cut power](/asimov/1/operate/safety/stopping#cutting-power). The firmware stays stopped until [First Start](/asimov/1/operate/setup/first-start).
Before You Start [#before-you-start]
* The [Finish Assembly](/asimov/1/build/assembly-steps/finish-assembly) checks pass, with every actuator in the joint matching its sticker number. Inspect wiring with the battery disconnected, and never connect or reseat an actuator cable while powered.
* On the **Firmware** page, the **RPU** card's **Network** and **Control** checks are both healthy.
* The firmware is **stopped**. On a robot that has run before, stop it from **Firmware** first.
* You can pose the robot at its home position and hold it still. It is easiest with two people: one holds the pose, the other works the console.
Open **Actuators**. Its tools work only while the firmware is stopped and the RPU is reachable, because the running firmware owns the actuators' CAN buses. If the page shows a lock pill such as **Stop the firmware to change actuators**, follow its **Open Firmware ›** link, fix the cause, then select **Retry**.
The page has three tabs — **Actuator ID**, **Liveness Check** and **Calibration**. You can open them in any order; nothing tracks your progress, so the checks below are yours to make.
Actuator IDs Are Already Assigned [#actuator-ids-are-already-assigned]
Each actuator's ID tells it which joint it is. IDs are assigned **before assembly**, one bare actuator at a time on the setup connector, with the **Actuator ID** tab. On an assembled robot this is already done.
Assigning an ID permanently programs whichever actuator is on the setup connector, and an assembled harness has no safe way to single one out. If IDs were never assigned, follow [Assign Actuator IDs](/asimov/1/build/assembly-preparations/assign-actuator-ids) with the actuators loose on the bench.
Check Liveness [#check-liveness]
Open the **Liveness Check** tab and select **Check Liveness**. Nothing moves; this only reads.
You want **25 of 25** responding, with every joint green on the map. Orange means alive but reporting an error; red means no response.
| Column | Meaning |
| --------- | ------------------------------------------------------------------------------------------ |
| Actuator | The ID assigned before assembly |
| Joint | The joint that ID belongs to |
| Status | **alive** or **no response**. The badge turns orange when a live actuator reports an error |
| Pos (rad) | Current position, in radians |
| Temp °C | Actuator and driver temperature |
| Error | The fault it is reporting, if any |
**Don't continue until all 25 rows are healthy.** Nothing in the software stops you from carrying on with a short count, and an actuator that doesn't answer here becomes an uncontrolled joint later.
| What you see | What to do |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| A joint shows **no response** | Power down, then inspect its power and CAN connections. The ID tells you where in the chain to look. Restore power and check again |
| A joint is alive but reporting a fault | Resolve the fault first. If it is overheating, let it cool, then check again |
| Far fewer than 25 respond | Suspect a whole branch: each limb is one daisy chain running outward from the body |
Calibrate [#calibrate]
Calibration defines the robot's home pose: each actuator stores its current position as zero, and every joint angle is measured from there. A joint calibrated while drooping is off by that much on every move afterwards.
This guide does not yet specify the joint-by-joint home-pose angles or an alignment fixture. Don't assume that any upright or straight-looking pose is correct. If you don't have the confirmed home-pose reference for your robot, stop here and get it first.
{/* TODO(hardware): home-pose reference — joint-by-joint angles, photos, or an alignment fixture. Setup cannot be completed without it. */}
. Open the **Calibration** tab.
. Pose the robot at its confirmed home position. The joints move freely by hand, so support each limb rather than expecting it to stay put.
. Read the warning and select **I Understand, Enable Calibration**.
. Under **Adjust one joint**, pick a joint, select **Calibrate**, then **Calibrate Actuator** to confirm. The robot reads the new zero back and marks the joint **calibrated** only if it landed within 0.02 rad.
. Repeat for all 25 joints — there is no bulk action. Redo any joint marked **failed** or **calibrated, unverified**. Calibrating a joint again simply overwrites its zero.
Verify Before Starting the Firmware [#verify-before-starting-the-firmware]
Keep the robot supported at the home pose, with the firmware stopped.
. Every joint on the map reads **calibrated**. Treat any other result as unfinished.
. Run **Check Liveness** again: all **25 of 25** respond, with no errors.
. Each joint's **Pos (rad)** reads close to 0 while the robot is still at the home pose. Investigate any joint that doesn't.
. No limb sagged or shifted during calibration. If one did, restore the pose, recalibrate that joint and check again.
These checks confirm reported health and the recorded zeros. They are not a motion test — don't start the firmware and drive the robot as a shortcut to checking calibration.
Recalibration After Repairs [#recalibration-after-repairs]
IDs and zero points are stored in the actuators themselves, so they survive a restart, a Raspberry Pi 5 reflash and a software update. Recalibrate a joint only when its zero is wrong:
* **After replacing an actuator.** Assign its ID with the single-actuator [bench procedure](/asimov/1/build/assembly-preparations/assign-actuator-ids) first.
* **When a joint was calibrated in the wrong pose.**
* **When the robot drifts, stumbles or leans** and the liveness check and firmware log show nothing wrong.
Stop the firmware, then check liveness and calibrate the affected joint at the home pose. Check all 25 before starting the firmware again.
Recalibration doesn't fix wiring faults, an actuator that stops responding or a robot that damps itself — calibrating over those only hides the evidence. Diagnose them first with **Check Liveness** and the [firmware log](/asimov/1/operate/troubleshooting#reading-the-logs).
**Next:** [First Start](/asimov/1/operate/setup/first-start).
# Connect & Sign In
Asimov Manager is the console the robot serves from its Raspberry Pi 5. This page gets you into it, onto your Wi-Fi and off the default password. If you already used Asimov Manager during assembly, check you can still reach it, set a password if you haven't, and move on to [Calibrate Actuators](/asimov/1/operate/setup/calibrate-actuators).
Find the Robot [#find-the-robot]
Power the robot on and wait about two minutes. The Pi 5 then either joins a saved Wi-Fi network — such as the one you entered in Raspberry Pi Imager when [flashing it](/asimov/1/build/assembly-preparations/software-setup/flash-raspberry-pi) — or starts its own setup hotspot.
**If the robot joined your Wi-Fi**, open this address from a device on the same network:
```
http://.local
```
`` is the hostname you set when flashing the Pi 5.
**If it didn't**, join the setup hotspot from your laptop or phone:
* **Network name:** `asimov-XXXX`, where `XXXX` is the last four characters of the Pi 5 's Wi-Fi MAC address.
* **Password:** the 16-digit code from the Pi 5 's retail box — see [Firmware & Software Flashing](/asimov/1/build/assembly-preparations/software-setup).
Then open `http://.local`. If `.local` doesn't resolve — common on Android — use the hotspot's address, usually `http://10.42.0.1`. When the robot has a saved network it can't reach, the hotspot appears about 90 seconds later than usual.
Sign In [#sign-in]
The default login is pre-filled until you set a password, so signing in is one click. Asimov Manager opens on **Overview**, with the **Finish setting up** checklist in the sidebar.
Join Your Wi-Fi [#join-your-wi-fi]
Skip this if the robot is already on your network.
While the setup hotspot is on, the robot can't scan for networks, so there is no list to pick from — you type your network's name. When the robot starts joining, the hotspot and this page drop. That is expected.
. Open **Wi-Fi** in the sidebar and select **Other Network…**.
. Enter your network's name exactly as broadcast, and its password. Leave the password blank for an open network.
. Note the address in the dialog — `http://.local` — or select **Copy address**. Then select **Join**.
. Move your device back to your own Wi-Fi and open that address.
If the robot can't join, the setup hotspot comes back on its own; join it and try again. If the console never loads on your network, see [The Console Will Not Load](/asimov/1/operate/troubleshooting#the-console-will-not-load).
Changing Networks Later [#changing-networks-later]
Once the robot is on a network, the Wi-Fi page lists nearby networks with their signal strength. Select one to join it; **Rescan** refreshes the list. Saved networks are under **Saved networks**, each with a **Forget** button. Forgetting the last one turns the setup hotspot back on.
Set a Password [#set-a-password]
The default login is public knowledge, and anyone on your network can reach the console.
. Open the user menu at the bottom of the sidebar and select **Account**.
. Enter the current password, then your new password twice.
. Select **Save changes**.
This clears the **Set a password** row and the default-password warning. There is no strength rule, so choose a strong password yourself. If you forget it, see [Forgotten Password](/asimov/1/operate/asimov-manager#forgotten-password).
Check the RPU Link [#check-the-rpu-link]
The checklist's first row, **Reach the RPU**, should now be done: Asimov Manager can control the Robot Processing Unit (RPU), whose Radxa CM5 runs the motion-control firmware. If the row isn't done, open **Firmware** and read the **RPU** card's two checks:
* **Network** failing: check the Ethernet cable between the Pi 5 and the Radxa Carrier Board in the RPU, and that both the Head Compute Unit and RPU have power. Power down before reseating connections.
* **Control** failing: the RPU is still booting or its services are down. Wait a minute, then reload.
[The RPU Is Unreachable](/asimov/1/operate/troubleshooting#the-rpu-is-unreachable) covers the rest.
**Next:** [Calibrate Actuators](/asimov/1/operate/setup/calibrate-actuators).
# First Start
Two steps remain, and they are deliberately separate: **confirming assembly** records the robot as built, and **starting the firmware** powers its actuators.
Before You Start [#before-you-start]
* All **25 of 25** actuators respond without faults, and every joint is calibrated at the confirmed home pose.
* The robot hangs from its gantry hook or sits on a bench, the area is clear and hands are out of the joints.
* On the **Firmware** page, the **RPU** card's **Network** and **Control** checks are both healthy.
* You know how to [cut power](/asimov/1/operate/safety/stopping#cutting-power).
Confirm Assembly [#confirm-assembly]
The firmware won't start until assembly is confirmed. Open **Firmware**; until you confirm, it shows a banner asking **"Is the robot fully assembled?"**
. Select **Confirm assembly**.
. Select **Check now** to sweep every actuator; it takes about 15 seconds. You want **25 of 25 responding**. A result such as "Actuator 7 isn't responding" sends you back to [Calibrate Actuators](/asimov/1/operate/setup/calibrate-actuators).
. Type `CONFIRM` and select **Confirm assembly**. The page shows "Recorded. The firmware can be started now."
Confirming records the robot as assembled on the RPU, so the record survives a Raspberry Pi 5 reflash, and sets the robot's onboard services, including Asimov Edge, to start on boot. It doesn't start the firmware, and it leaves the firmware's own **Start on boot** switch off.
Reversing a confirmation takes `asimovctl reset-setup` on the command line — see [Command Line](/asimov/1/operate/asimov-manager#command-line). Confirm only a robot that is fully assembled, wired and calibrated.
Start the Firmware [#start-the-firmware]
The actuators come on in Damp — powered but limp — so the robot can't hold itself up. Keep it supported and keep clear of the joints.
On the **Firmware** page, select **Start Firmware** and confirm. From now on you can also start the robot with **Start Robot** on Overview — see [Power On and Off](/asimov/1/operate/power).
**Stop Firmware** is always available while the firmware runs, whatever else is wrong.
Check It Came Up Healthy [#check-it-came-up-healthy]
Open **Overview**:
| Check | Healthy |
| --------------- | ------------------------------------------------------------- |
| Firmware | **Running** in the subsystem strip |
| Actuators | **25 of 25 responding** |
| Battery | A percentage. A blank reading means Asimov Edge isn't running |
| Setup checklist | Gone from the sidebar |
If anything is off, see [Troubleshooting](/asimov/1/operate/troubleshooting). Leave the firmware's **Start on boot** switch off for now; [Start on Boot](/asimov/1/operate/power#start-on-boot) explains why.
**Next:** [Stand Up and Walk](/asimov/1/operate/drive/stand-up) — the robot is commissioned.
# Set Up Overview
Set Up takes an assembled robot to one you can drive: on your network, all 25 actuators verified and calibrated, assembly confirmed and the firmware running. You do it once per robot, and again only after repairs.
Before You Start [#before-you-start]
* **Assembly is finished** and the [Finish Assembly](/asimov/1/build/assembly-steps/finish-assembly) checks pass.
* **Both computers are flashed** — see [Firmware & Software Flashing](/asimov/1/build/assembly-preparations/software-setup).
* **All 25 actuator IDs are assigned.** That happens before assembly, one bare actuator at a time — see [Assign Actuator IDs](/asimov/1/build/assembly-preparations/assign-actuator-ids). If it never happened, stop: an ID can't be assigned safely once the harness is in place.
* **You have read [Safety & Handling](/asimov/1/operate/safety).** Calibration and first start both involve supporting the robot.
The Steps [#the-steps]
**[Connect & Sign In](/asimov/1/operate/setup/connect).** Reach Asimov Manager in a browser, put the robot on your Wi-Fi and replace the default password.
**[Calibrate Actuators](/asimov/1/operate/setup/calibrate-actuators).** Check that all 25 actuators respond, and record each one's zero position at the home pose. The firmware stays stopped.
**[First Start](/asimov/1/operate/setup/first-start).** Confirm assembly, start the firmware for the first time and check that the robot came up healthy.
The Setup Checklist [#the-setup-checklist]
Asimov Manager tracks the same work in the sidebar's **Finish setting up** checklist:
| Row | Done when | Covered in |
| -------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Reach the RPU | Asimov Manager can control the Robot Processing Unit (RPU) | [Connect & Sign In](/asimov/1/operate/setup/connect#check-the-rpu-link) |
| Set up the actuators | Assembly is confirmed | [Calibrate Actuators](/asimov/1/operate/setup/calibrate-actuators), then [First Start](/asimov/1/operate/setup/first-start#confirm-assembly) |
| Start the firmware | The firmware is running | [First Start](/asimov/1/operate/setup/first-start#start-the-firmware) |
| Set a password | The default password is replaced | [Connect & Sign In](/asimov/1/operate/setup/connect#set-a-password) |
| Join Wi-Fi | Shown only while the robot is on its setup hotspot | [Connect & Sign In](/asimov/1/operate/setup/connect#join-your-wi-fi) |
Rows that depend on the RPU stay greyed out ("Waiting on the RPU") until it is reachable, so fix the link first. **Skip setup** hides the checklist in your browser, and **Show the setup guide** on the Account page brings it back. The checklist closes once the robot is fully set up.
**Next:** [Connect & Sign In](/asimov/1/operate/setup/connect).
# Cockpit Controls
Once the robot is in Walk — see [Stand Up and Walk](/asimov/1/operate/drive/stand-up) — drive it from the Cockpit, on a laptop or a phone. The camera panel shows **No video** in this release, so drive within sight of the robot.
How Input Works [#how-input-works]
* **Input is held.** Release a key, a touch pad or the stick and the robot's velocity goes to zero. It doesn't damp.
* **Further is faster.** How far you push sets the speed, up to your [speed limits](#speed-limits).
* **Losing focus is a stop.** If the Cockpit tab loses focus, input clears and velocity goes to zero; you keep control. Keys do nothing while a dialog or the menu is open.
* **Missing input is a stop.** If Asimov Manager hears nothing from your browser for two seconds, it sets the velocity to zero.
* **A dropped connection ends your control.** The Cockpit shows "Cockpit connection lost · reconnecting…" and reconnects without control; take control again.
Keyboard [#keyboard]
On a desktop:
| Key | Action |
| --------- | ------------------------------------------------------------ |
| `W` / `S` | Forward / back |
| `A` / `D` | Strafe left / right |
| `Q` / `E` | Turn left / right |
| `Shift` | Half speed, while held |
| `Space` | Pause — see [Pause](/asimov/1/operate/safety/stopping#pause) |
| `X` | E-Stop |
The drag pad beside the keys does the same with a mouse: drag the knob further to go faster.
Phone [#phone]
* **JOYSTICK** (the default): the **DRIVE** pad on the left moves forward, back and sideways; the **TURN** pad on the right turns; **PAUSE** sits between them. Touch and drag, and release to stop.
* **BUTTONS**: a D-pad and rotate keys that drive at your full speed limit. The Cockpit returns to JOYSTICK on your next visit.
* **Landscape**: turn the phone sideways for a full-screen driving view. Set the control mode and speed limits in portrait first — landscape has neither.
On a phone, the Cockpit has no E-STOP button of its own: use **E-STOP** in the top bar — press and hold.
Speed Limits [#speed-limits]
Select the speed chip (it reads like **1.0 M/S · 57°/S**) to open two sliders:
* **Max speed** caps forward, back and sideways speed, up to 1.0 m/s.
* **Turn speed** caps turning, up to 57°/s.
Both start at their maximum. Lower them before your first sessions — the robot has real momentum. The limits are saved in your browser only, so set them again on another device.
`Shift` halves your speed while held. A gamepad drives at half speed until you hold its turbo trigger, and never exceeds these limits.
Telemetry and Logs [#telemetry-and-logs]
Select **Logs** in the Cockpit header to open the information pane. It docks beside the camera on a desktop and slides up as a sheet on a phone.
* **Telemetry**: control mode, command sequence and error flags; position, velocity, current and temperature for all 25 joints; the IMU; and battery charge, voltage, current, maximum cell temperature and protection flags. It updates live.
* **Logs**: the newest 40 entries from Asimov Edge or the firmware, refreshed every two seconds. **Full logs** opens the [Troubleshoot page](/asimov/1/operate/troubleshooting#reading-the-logs).
The Cockpit's status line shows the robot's control mode, and **fault N** when the firmware reports a fault — see [Recovering After an E-Stop or a Fall](/asimov/1/operate/safety/stopping#recovering-after-an-e-stop-or-a-fall).
If You Cannot Drive [#if-you-cannot-drive]
| What you see | What to do |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| The posture controls are disabled | Take control first — see [Take Control](/asimov/1/operate/drive/stand-up#take-control) |
| "Switch to Walk to drive" | The robot moves only in Walk. [Stand it up](/asimov/1/operate/drive/stand-up#stand-up) first |
| **Start Asimov Edge** in place of the controls | Asimov Edge, the service linking the console to the robot, isn't running. Follow the link and start it on the Firmware page |
| "Waiting for the previous Cockpit session to release…" | Another browser has the Cockpit open. Close it there |
| **Gamepad in control** | A gamepad paired to the robot is driving. **Take Control** to override it — see [Gamepad](/asimov/1/operate/drive/gamepad#who-drives) |
| "Cockpit connection lost · reconnecting…" | Wait for it to reconnect, then take control again |
| The robot drifts or stumbles in Walk | Suspect a zero point — see [Recalibration After Repairs](/asimov/1/operate/setup/calibrate-actuators#recalibration-after-repairs) |
Driving from Code [#driving-from-code]
The [SDK](/asimov/1/program/sdk) and the [Asimov API](/asimov/1/program/api) can do everything the Cockpit does, using credentials issued on the [Developer page](/asimov/1/operate/asimov-manager#developer-credentials). The robot takes commands from one controller at a time.
# Gamepad
A gamepad can drive Asimov 1 in two ways, and they behave differently:
| | Paired to the robot | Connected to your browser |
| ---------------------- | ----------------------------- | ---------------------------------------------- |
| Connects to | The robot, over Bluetooth | Your computer, over USB or Bluetooth |
| Needs the Cockpit open | No | Yes, with a session held and the robot in Walk |
| Lasts | Stays paired between sessions | This page visit only |
| Controls | Drive, postures and E-Stop | Drive and E-Stop. Postures stay on screen |
Either way, the Cockpit's rules apply: the robot moves only in Walk, releasing the stick stops it, and your [speed limits](/asimov/1/operate/drive/cockpit#speed-limits) cap everything. Read [Stand Up and Walk](/asimov/1/operate/drive/stand-up) first.
Pair a Gamepad to the Robot [#pair-a-gamepad-to-the-robot]
Open **Controller** in the sidebar. Nearby gamepads appear automatically while the page is open.
. Check that the **Bluetooth** row is on. If it shows **Radio blocked** or **Not responding**, select **Restart** — this drops any connected device.
. Put the gamepad in pairing mode, wait for it to appear, and pair it.
. Once connected, it shows on the sidebar's device card, and the Cockpit announces it.
The **Diagnostics** section helps when pairing misbehaves. Asimov Manager treats any Bluetooth device connected to the robot as a gamepad, so disconnect other devices you aren't driving with.
Who Drives [#who-drives]
A Cockpit session outranks a paired gamepad; with no session open, the gamepad drives.
* While the gamepad drives, the Cockpit shows **Gamepad in control**. **Take Control** takes over from it.
* While you hold a session, the Cockpit shows **Gamepad Detected** with an **Enable** button. **Enable** ends your session and hands control to the gamepad.
A gamepad connected to your browser shows as **Gamepad detected on this computer**. **Enable** arms it for this page visit; it drives only once you hold a session and the robot is in Walk. Disconnecting it or leaving the page disarms it.
Browser Gamepad Controls [#browser-gamepad-controls]
These follow the standard gamepad layout:
| Control | Xbox | PlayStation | Action |
| ------------------ | ---------- | ----------- | --------------------------------------------- |
| Left stick | Left stick | Left stick | Drive: forward, back and sideways |
| Bottom face button | `A` | `✕` | Brake: hold for zero velocity |
| Left bumper | `LB` | `L1` | Turn left |
| Left trigger | `LT` | `L2` | Turn right |
| Right trigger | `RT` | `R2` | Turbo: full speed while held, instead of half |
| Menu button | `☰` | `Options` | E-Stop |
Paired Gamepad Controls [#paired-gamepad-controls]
The Cockpit's **Controller Guide** appears whenever a gamepad drives. Dismiss it with its ✕; the ⓘ on the gamepad pill brings it back. It lists:
| Control | Action |
| --------------- | -------------------------------------------------------------- |
| Left stick | Drive: forward, back and sideways |
| `L1` / `L2` | Turn left / right |
| `R2` / `ZR` | Turbo, while held |
| `✕` / `A` | Brake: hold for zero velocity |
| `X` | **Damp** — releases all actuators; the robot must be supported |
| `A` | **Stand** |
| `B` | **Walk** |
| `Options` / `☰` | **E-Stop** |
The guide mixes button names from different gamepad makers, and lists `A` for both Brake and Stand. Before your first drive, try each button with the robot supported on its gantry hook, so you know which one brakes and which one changes posture.
{/* TODO(edge): SAFETY — the paired-pad bindings live in Asimov Edge, not asimov-manager. Confirm the real mapping for each supported gamepad family, fix the Controller Guide's A/✕ collision, and replace the table above with one column per family. */}
E-Stop from a Gamepad [#e-stop-from-a-gamepad]
`Options` / `☰` damps the robot exactly like the console's E-Stop: at once, and a free-standing robot falls. It works even while a dialog is open. To recover, see [Recovering After an E-Stop or a Fall](/asimov/1/operate/safety/stopping#recovering-after-an-e-stop-or-a-fall).
# Drive from Python
`menlo-sdk` drives Asimov 1 from Python on your computer: the same Damp, Stand and Walk
sequence as the Cockpit, plus the camera, the microphone and the speaker. This page takes a
freshly built robot from its gantry hook to walking under a script, and back. Everything on
it comes from the example scripts in the SDK repository, which the
[Python SDK](/sdk) section explains line by line.
Keep Asimov Manager open at the **E-Stop** whenever the robot stands, and the battery unit
within reach to cut power ([Stopping the Robot](/asimov/1/operate/safety/stopping)). Every
command a script sends passes through the robot's safety layer, and the Cockpit and a paired
gamepad outrank it: close the Cockpit and unpair the gamepad before you run a script.
Before You Start [#before-you-start]
* The robot is assembled, powered and signed in to Asimov Manager, and you have driven it
once from the Cockpit ([Stand Up and Walk](/asimov/1/operate/drive/stand-up)).
* The robot hangs from its gantry hook with both feet on the floor and 2 m of clear floor
ahead. The keyboard step also walks it backward and sideways: for that, keep clear floor
all around it.
* Your computer is on the same network as the robot and runs Python 3.12 or newer.
Choose How the SDK Connects [#choose-how-the-sdk-connects]
The SDK reaches the robot in one of three connection modes. Pick one now; the rest of the
page tells you which step each one needs.
| Connection mode | Commands and state travel | Camera and audio | Needs |
| --------------- | -------------------------------- | ---------------- | ----------------------------------------------------- |
| `udp` | over the robot's network | no | UDP control turned on and state sent to your computer |
| `hybrid` | over the robot's network | yes | the same, plus an SDK credential |
| `livekit` | through the robot's LiveKit room | yes | an SDK credential with the Control role |
Choose `udp` to drive only, `hybrid` to drive and use the camera and microphone from the
same script, and `livekit` when your computer is not on the robot's network. Details are in
[Connection Modes](/sdk/connection-modes).
Get an SDK Credential [#get-an-sdk-credential]
The `hybrid` and `livekit` connection modes join the robot's LiveKit room with an SDK
credential from Asimov Manager. Skip this step for `udp`.
. In Asimov Manager, open **Developer** in the sidebar. Under **How a script connects**, note the **Manager URL**: it is the address the SDK uses to reach the robot.
You do not need the **LiveKit URL** or the **Room**. The SDK asks the robot for both each time it connects, and when the LiveKit URL reads `localhost`, the SDK uses the robot's address instead.
. Under **Issue a credential**, enter a **Name** for the computer that will use it, such as `my-laptop`. Issue one per computer, so you can revoke one without cutting off the others.
. Under **What it may do**, choose **Control** to drive the robot from your computer. **Observe** watches the camera and listens to the microphone only; the robot drops its commands.
. Select **Issue credential**, then copy the credential with the copy button next to it. It is shown only this once: the robot keeps the credential's ID, not the credential, so it cannot show it again. If you lose it, revoke it and issue another.
. The credential appears under **Issued credentials** with its role and ID. To take it back, select **Revoke** next to it; the computer holding it can no longer connect, and a session that is already running keeps working until its LiveKit token expires, at most 12 hours.
Turn On UDP Control [#turn-on-udp-control]
The `udp` and `hybrid` connection modes carry commands over the robot's network and the
robot sends its state to one computer. Skip this step for `livekit`.
. In Asimov Manager, open **Advanced Settings** in the sidebar and find the **Asimov Edge**
section.
. Set `udp-control` to on.
. Set `udp-state-host` to your computer's address on the robot's network. Leave
`udp-state-port` at its default, 8851.
. Select **Save & Restart Asimov Edge** and wait for the robot to come back.
Install the SDK and Save the Robot [#install-the-sdk-and-save-the-robot]
. On your computer, install the package:
```bash
pip install menlo-sdk
```
. Save the robot:
```bash
menlo setup
```
The wizard asks for a name, the connection mode you chose, and what that mode needs: the
robot's address for `udp`, the **Manager URL** from the Developer page and the credential
for `livekit`, both for `hybrid`. It checks each field against the robot before it
saves, so a wrong address or a revoked credential shows up here, not in a script.
. Check that the SDK sees the robot:
```bash
menlo status
```
The panel shows the robot mode, whether the state stream is fresh, the battery and any
fault, and a READY or NOT READY badge with the first reason. It sends nothing.
. Get the example scripts:
```bash
git clone https://github.com/menloresearch/menlo-sdk.git
cd menlo-sdk/examples
```
Check the Robot from a Script [#check-the-robot-from-a-script]
`check.py` connects, prints what the robot reports (robot mode, arming, faults, alerts, the
hottest joint, battery) and `preflight()` for a stand, a walk and a trajectory, and sends
nothing. Run it before anything that moves the robot:
```python lineNumbers=16
# The SDK counts the 0.5 s a robot in STAND must be upright to arm from its own samples.
time.sleep(0.6)
s = robot.get_state() # one sample: every fact below comes from it
print(f"robot mode {s.mode.name}, armed {robot.armed}, faulted {s.faulted}")
print(f"alerts {', '.join(a.name for a in s.alerts) or 'none'}")
temps = [j.temp for j in s.joints if j.temp is not None]
print(f"hottest joint {max(temps):.0f} C" if temps else "joint temperatures not reported")
print(f"battery {s.battery.soc_percent:.0f} %" if s.battery else "battery not reported")
print(f"state {s.age_s:.2f} s old")
for action in ("stand", "move", "trajectory"):
print(robot.preflight(action)) # "ready to move" means live state, and the facts
```
```bash
python check.py
```
The SDK refuses a command only when there is no live state, and the firmware decides the
rest. Every script that moves the robot calls `guard(robot)` from `guard.py` first, an
optional guard of your own that stops the script on a fault, an alert, a hot actuator or a
low battery. Edit its limits, or delete the line; see
[Write Your Own Guard](/sdk/safety#write-your-own-guard).
Walk It Around [#walk-it-around]
The scripts move the robot through the same three robot modes as the Cockpit's postures:
DAMP, STAND and MOVE. STAND holds a pose without a balance loop, so the robot stays on its
gantry hook until it is in MOVE; see [Standing and Balancing](/sdk/safety#standing-and-balancing).
. With the robot in DAMP on its gantry hook, stand it up:
```bash
python stand.py
```
The actuators take the standing pose and the script returns once the robot is armed:
STAND held upright for 0.5 s.
```python lineNumbers=19
guard(robot) # your rule, in guard.py; delete this line to stand without it
try:
# Returns once the robot reports STAND and has been upright for 0.5 s (armed): the
# firmware enters MOVE only after that.
robot.stand()
except (NotReadyError, WaitTimeoutError) as exc: # no live state, a fault, or not armed
print(exc) # what the robot reports and what to do
sys.exit(1)
print(f"robot mode {robot.get_state().mode.name}, armed {robot.armed}")
```
. Balance it:
```bash
python balance.py
```
The robot enters MOVE at zero velocity and the walking policy balances it in place.
```python lineNumbers=18
guard(robot) # your rule, in guard.py; delete this line to balance without it
try:
robot.balance() # returns once the robot reports MOVE
except (NotReadyError, WaitTimeoutError) as exc: # no live state, a fault, or not MOVE
print(exc) # what the robot reports and what to do
sys.exit(1)
print(f"robot mode {robot.get_state().mode.name}, balancing in place")
```
. Slacken the gantry gradually until the robot carries its own weight.
. Drive it from the keyboard. The keyboard walks the robot backward, sideways and round as
well as forward, so keep clear floor all around it:
```bash
python keyboard.py
```
| Key | Effect |
| --------- | -------------------------- |
| `w` / `s` | walk forward / back |
| `a` / `d` | strafe left / right |
| `q` / `e` | turn left / right |
| space | balance in place |
| `t` | stand (asks first in MOVE) |
| `b` | damp, after asking |
| `x` | quit with zero velocity |
Each key press holds its velocity for about 0.3 s, so releasing a key stops the robot.
Press space somewhere level, with the robot not mid-stride, before you go on. `walk.py`
does the same without the keyboard: 0.3 m/s forward for 3 s, then balance in place.
```python lineNumbers=181
while True:
show(robot, last)
key = next_key(TICK_S)
if key is None:
continue
if key in QUIT:
break
if key in MOVES:
last = move(robot, key)
elif key == " ":
last = balance(robot)
elif key == "t":
last = stand(robot, next_key)
elif key == "b":
last = confirm_and_damp(robot, next_key)
```
. Support the robot: take up the gantry's slack so it holds the robot again.
. Damp it:
```bash
python damp.py
```
The script shows the robot mode and asks `Is the robot supported? Damp now? [y/N]`.
Answer `y` only with the robot supported: every actuator stops holding its position and a
free-standing robot falls.
```python lineNumbers=17
# damp() is never refused, so there is no check here, only the question.
if not YES:
mode = robot.get_state().mode.name
question = f"Robot mode {mode}. Is the robot supported? Damp now? [y/N] "
if input(question).strip().lower() not in ("y", "yes"):
print("nothing sent")
sys.exit(0)
try:
robot.damp() # returns once the robot reports DAMP
except WaitTimeoutError as exc: # sent, but DAMP was not reported: the message says what next
print(exc)
sys.exit(1)
print(f"robot mode {robot.get_state().mode.name}")
```
To go through STAND before DAMP, as the Cockpit does, run `python rest.py` instead: it asks
whether the robot is hanging from its gantry hook or seated on a stool or bench, then goes
MOVE, STAND, DAMP. See [Bringing the Robot to Rest](/sdk/safety#bringing-the-robot-to-rest).
The same four steps run from the terminal without a script: `menlo stand`, `menlo balance`,
`menlo walk --vx 0.3 --duration 3` and `menlo damp`; each one asks before it sends. See
[Command Line](/sdk/cli).
Record Video [#record-video]
The camera, the microphone and the speaker come through the robot's LiveKit room, so this
step and the next two need the `hybrid` or `livekit` connection mode. The scripts send no
motion command and run whatever robot mode the robot is in.
`camera_and_audio.py` saves a photo, then a 3 s clip as one JPEG per frame in `clip/` with
its sound in `clip.wav`, then plays a tone on the speaker:
```bash
pip install pillow
python camera_and_audio.py
```
```python lineNumbers=26
if robot.has("camera"):
photo = robot.camera.photo() # the next fresh frame, rgb8
Path(PHOTO).write_bytes(photo.to_jpeg())
print(f"saved {PHOTO}, {photo.width}x{photo.height}")
clip = robot.camera.capture_clip(CLIP_S, audio=robot.has("microphone"))
frames = clip.save_frames(CLIP_DIR)
print(f"saved {len(frames)} frames to {CLIP_DIR}/ at {clip.fps:.1f} fps")
if clip.audio:
print("saved", clip.save_wav(CLIP_WAV))
else:
print("this connection carries no camera; use hybrid or livekit")
if robot.has("speaker"):
# 16-bit mono PCM: a sine wave at TONE_HZ for TONE_S.
tone = b"".join(
struct.pack("
[Camera and Audio](/sdk/media) covers frames, clips and MP4 output.
Record the Microphone [#record-the-microphone]
`record_audio.py` records 3 s from the robot's microphone into `microphone.wav`:
```bash
python record_audio.py
```
```python lineNumbers=18
if not robot.has("microphone"):
print("this connection carries no microphone; use hybrid or livekit")
sys.exit(1)
# Every chunk in order, as 16-bit PCM. chunks() raises WaitTimeoutError when the
# microphone says nothing for its timeout.
chunks: list[AudioChunk] = []
recorded_s = 0.0
for chunk in robot.microphone.chunks(timeout=5.0):
chunks.append(chunk)
recorded_s += chunk.duration_s
if recorded_s >= SECONDS:
break
with wave.open(PATH, "wb") as wav:
wav.setnchannels(chunks[0].channels)
wav.setsampwidth(2) # 16-bit samples
wav.setframerate(chunks[0].sample_rate_hz)
wav.writeframes(b"".join(c.data for c in chunks))
print(f"saved {PATH}, {recorded_s:.1f} s at {chunks[0].sample_rate_hz} Hz")
```
Play a Sound on the Speaker [#play-a-sound-on-the-speaker]
`play_audio.py` plays a 16-bit PCM WAV file, `hello.wav`, through the robot's speaker in 1 s
chunks. It reads `hello.wav` from the directory you run it from; with no file there, it plays a
one-second 440 Hz tone. Over `hybrid` and
`livekit`, the credential needs the Control role for the speaker:
```bash
python play_audio.py
```
```python lineNumbers=24
if not robot.has("speaker"):
print("this connection carries no speaker; use hybrid or livekit")
sys.exit(1)
if os.path.exists(PATH):
with wave.open(PATH, "rb") as wav:
if wav.getsampwidth() != 2:
print(f"{PATH} is not 16-bit PCM; convert it first")
sys.exit(1)
rate, channels = wav.getframerate(), wav.getnchannels()
pcm = wav.readframes(wav.getnframes())
name = PATH
else: # 16-bit mono PCM: one second of a 440 Hz sine wave
rate, channels, name = 16_000, 1, f"a 440 Hz tone (no {PATH} here)"
pcm = b"".join(
struct.pack("
Related [#related]
* [Python SDK](/sdk): every page of the SDK guide
* [Quickstart](/sdk/quickstart): the stand, balance and walk scripts, line by line
* [Safety](/sdk/safety): what the SDK reports, your own guard, and the watchdogs
* [Stand Up and Walk](/asimov/1/operate/drive/stand-up): the same sequence from the Cockpit
* [Asimov Manager](/asimov/1/operate/asimov-manager#developer-credentials): where credentials live
# Stand Up and Walk
Every drive starts and ends here. The robot is always in one of three postures — **Damp**, **Stand** and **Walk** — and you move between them one step at a time. This page takes a supported robot to walking on its own, and back.
Keep the robot supported until it is in Walk, and support it again before you leave Walk. Clear the area, keep the robot in sight, and know your [stops](/asimov/1/operate/safety/stopping).
Before You Start [#before-you-start]
* The firmware is running: Overview shows **Drive Robot**. If it doesn't, see [Start the Robot](/asimov/1/operate/power#start-the-robot).
* The robot hangs from its gantry hook with both feet flat on a clear, level floor.
* For your first sessions, lower **Max speed** well below its default — see [Speed Limits](/asimov/1/operate/drive/cockpit#speed-limits).
{/* TODO(hardware): confirm the stand-up setup — gantry height and slack, and whether a robot can stand up from a bench. */}
Take Control [#take-control]
Open **Cockpit** from the sidebar, or select **Drive Robot** on Overview.
. Select **Take Control**. The Cockpit asks: "Area clear? Check around the robot before it can move."
. Check around the robot, then confirm. On a desktop, select **Start Driving** before its three-second countdown ends. On a phone, press and hold **Start Driving** for one second.
You now hold the robot's only drive session, and the posture controls unlock. Taking control sends nothing to the robot by itself.
Only one Cockpit can be open at a time. A second browser waits with "Waiting for the previous Cockpit session to release…" until the first one leaves the page. A gamepad paired to the robot drives only when nobody holds a session — see [Gamepad](/asimov/1/operate/drive/gamepad).
The Three Postures [#the-three-postures]
| Posture | The Cockpit says | Support |
| --------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| **Damp** | "Releases all actuators instantly. The robot will fall unless it's seated on a bench or hanging from its gantry hook." | Required |
| **Stand** | "Actuators engage, the robot rises and balances on both feet." | Keep it on the gantry hook |
| **Walk** | "Accepts drive input — moves only when you drive it." | None, on a flat floor |
On a desktop, select a posture on the **Damp · Stand · Walk** switch. On a phone, the posture pill opens the **Robot posture** sheet. You can't skip a step: on a desktop, asking to go straight from Damp to Walk, or back, opens a guide that takes you through Stand; on a phone the far step stays locked.
{/* TODO(firmware): SAFETY-CRITICAL — confirm what Stand does. The Cockpit copy above says it balances; /sdk/safety says STAND stiffens a pose with no balance loop and tips a free-standing robot. The procedure on this page keeps the robot supported through Stand, which is safe either way. */}
Stand Up [#stand-up]
. With the robot still on its gantry hook, select **Stand**. The actuators engage and the robot takes its standing pose.
. Wait until the Cockpit shows the robot in Stand. Walk unlocks only once the robot reports it.
Start Walking [#start-walking]
. Select **Walk** (**Start Driving** on a phone). The robot balances in place and moves only when you drive it.
. Slacken the gantry gradually until the robot carries its own weight.
. Drive with the [Cockpit controls](/asimov/1/operate/drive/cockpit) or a [gamepad](/asimov/1/operate/drive/gamepad).
{/* TODO(hardware/firmware): confirm releasing the support in Walk is the intended sequence (the SDK safety page says to stay in MOVE at zero velocity rather than drop to STAND on a free-standing robot). */}
Bring It Back Down [#bring-it-back-down]
Never damp a free-standing robot — it falls.
. **Stop driving.** Select **PAUSE** or press `Space`, somewhere level, with the robot not mid-stride.
. **Support it.** Take up the gantry's slack so it holds the robot again.
. **Select Stand.** On a phone, press and hold **Hold to stand** for one second.
. **Select Damp.** Tick **The robot is physically supported**, then hold **Hold 2s to damp**.
On a desktop, selecting **Damp** straight from Walk opens a guide that takes you through Stand first.
End Your Session [#end-your-session]
On a desktop, select **End Control**. On a phone, leave the Cockpit — End Control isn't available there.
Ending your session stops the Cockpit sending commands. It doesn't change the robot's posture or stop the firmware, so bring the robot down before you leave. Then [turn it off](/asimov/1/operate/power#turn-the-robot-off) when you are done.
{/* TODO(edge): document what the robot does when the Cockpit's command stream stops (session ended, tab closed, network lost) — Asimov Edge's command lease owns this. */}
# Electrical
Overall Architecture [#overall-architecture]
The Compute Module comprises two connected stacks: the **Head Compute Unit** in the head and the **Robot Processing Unit (RPU)** in the torso.
| Subsystem | Role |
| ----------------- | ----------------------------------------------------------------------------------------------------------------- |
| Head Compute Unit | Provides media, networking, edge services, and the buzzer |
| RPU | Runs locomotion and actuator control, handles IMU feedback and actuator CAN communications, and distributes power |
| Actuator network | Carries battery power and CAN communication through the robot |
| Sensors and media | Provide inertial state, video, audio input, and audio output |
| Battery system | Supplies energy for actuators, compute, sensing, and peripherals |
Compute Module [#compute-module]
Head Compute Unit [#head-compute-unit]
The head stack contains three boards. The Head Board sits at the base, the Raspberry Pi 5 mounts above it on standoffs, and the Media HAT Board mounts on the Pi 5 header. The Media HAT Board provides the buzzer and helps prevent brownouts on the Pi 5 .
The **Head Compute Unit** is the electronics stack, not the entire mechanical head assembly. Its camera, mounts, covers, and neck hardware are identified separately in the build instructions.
Robot Processing Unit (RPU) [#robot-processing-unit-rpu]
The torso stack also contains three boards. The Radxa CM5 mounts on the Radxa Carrier Board , forming the **Motion Control Board (MCB)**. The Power Distribution Board (PDB) then fits above the Carrier Board . Together, the MCB and PDB form the **Robot Processing Unit (RPU)**.
The CM5 handles high-frequency, low-latency workloads, including locomotion, actuator control, and IMU feedback. The Carrier Board provides the interfaces for communication with all 25 actuators. The PDB distributes power to connected hardware.
Connecting the Two Assemblies [#connecting-the-two-assemblies]
A 1000 mm Ethernet cable links the Pi 5 to the Carrier Board . A separate 1000 mm XT30-Male to XT30-Female power cable connects the PDB to the Head Board . The Head Compute Unit and RPU work together while remaining in separate locations on the robot.
Keeping media, external networking, and application services on the Pi 5 separates those workloads from the CM5 's time-sensitive control loop.
Actuator Power and Communication [#actuator-power-and-communication]
The published system specification identifies five CAN interfaces at 1 Mbps and one at 500 kbps. The actuator network distributes battery power and CAN communication from the body outward through six physical branches.
Daisy-Chain Architecture [#daisy-chain-architecture]
Each actuator has two electrically equivalent `XT30 (2+2)` ports. Each port carries `BATT+`, `BATT-`, `CAN_H`, and `CAN_L`, allowing power and communication to continue from one actuator to the next.
There is no fixed electrical input or output port. Use the port and orientation shown in the applicable assembly step so the cable follows the intended mechanical path.
The six branches enter the body at left hip pitch, right hip pitch, waist yaw, left shoulder pitch, right shoulder pitch, and neck yaw. Within each branch, actuators form a chain from the central body toward the distal joint.
At each branch entry, `CAN_H` and `CAN_L` route toward the RPU while `BATT+` and `BATT-` route toward the robot power bus.
For example, each leg follows:
* `hip pitch -> hip roll -> hip yaw -> knee -> ankle A -> ankle B`
This topology keeps the number of body-entry connections small while allowing each limb to carry power and CAN through the same connector system.
Keep the battery disconnected and its output protected. Do not connect, disconnect, route, strip, or join conductors while any part of the robot is energized.
An interactive visualization will be added here to show how every wire connects through the actuator network.
The public sources do not yet reconcile the six physical branch-entry paths with the controller specification of five 1 Mbps CAN interfaces plus one 500 kbps interface. Treat the physical topology and controller-interface count as separate facts until the release-specific bus assignment is confirmed.
Sensors and Media Hardware [#sensors-and-media-hardware]
| Device | Current specification | Primary role |
| ---------- | ----------------------- | ------------------------------------------- |
| IMU | 6-axis | Body orientation and angular-motion sensing |
| Camera | 2 MP monocular camera | Robot video and visual input |
| Microphone | Stereo microphone array | Audio capture |
| Speaker | 10 W, 4 ohm | Robot audio output |
The Raspberry Pi 5 is the currently supported edge computer for camera, audio, networking, and edge services. Other Raspberry Pi models are not validated for Asimov 1.
Battery [#battery]
The current pack design is a 13-series, 4-parallel lithium-ion battery.
| Parameter | Current specification |
| -------------------------- | --------------------: |
| Battery type | Lithium-ion |
| Cell model | INR18650 |
| Pack configuration | 13S4P |
| Nominal voltage | 46.8 V |
| Typical capacity | 10.5 Ah |
| Minimum capacity | 10.05 Ah |
| Energy | 491.4 Wh |
| Charge voltage | 54.6 V |
| Discharge cut-off | 35.75 V |
| Standard charge current | 2.1 A |
| Maximum charge current | 15 A |
| Standard discharge current | 2.1 A |
| Maximum discharge current | 30 A |
| Storage voltage | 45.5 to 48.1 V |
| Battery mass | Approximately 2 kg |
Operating Temperature [#operating-temperature]
| Condition | Temperature range |
| ------------------------------------- | ----------------: |
| Charging | 0°C to 45°C |
| Discharging | -20°C to 60°C |
| Storage, less than 1 month | -10°C to 45°C |
| Long-term storage, less than 3 months | -10°C to 35°C |
Protection Circuit [#protection-circuit]
The pack includes a protection circuit module for over-charge, over-discharge, and over-current protection.
| Protection item | Threshold |
| ------------------------- | --------------: |
| Over-charge protection | 4.25 V per cell |
| Over-discharge protection | 2.75 V per cell |
| Over-current protection | 30 A or less |
# Overview
Explore the mechanical, electrical, and software systems that make up Asimov 1.
Mechanical [#mechanical]
The mechanical section explains how Asimov 1's modular body, 25 powered joints, and serviceable structure produce its physical form and movement.
**Information covered**
* dimensions, weight, and body modules
* joint layout, degrees of freedom, and motion ranges
* actuator families and the parallel-ankle mechanism
* materials, joint geometry, public CAD, and service points
Electrical [#electrical]
The electrical section follows power and data from the battery through the onboard computers, control electronics, sensors, media devices, and distributed actuators.
**Information covered**
* battery configuration and power architecture
* Compute Module architecture: the connected Head Compute Unit and RPU
* actuator power and CAN communications
* IMU, camera, microphone array, and speaker hardware
Software [#software]
The software section explains how Asimov 1 turns commands into motion while returning robot state, video, and audio to an application.
**Information covered**
* onboard compute and software responsibilities
* included locomotion policy and firmware control modes
* direct actuator control, 10 Hz telemetry, video, and audio interfaces
* simulation models, OTA updates, and Client SDK availability
# Mechanical
Explore the Design Interactively [#explore-the-design-interactively]
The public [Asimov 1 repository](https://github.com/asimovinc/asimov-1/tree/main/mechanical/ASV1) provides the mechanical design files.
Structural Architecture [#structural-architecture]
Asimov 1 is organized into the head, torso, pelvis, left and right arms, and left and right legs. These modules form the load-bearing frame and define the primary mechanical interfaces between body assemblies.
Form Factor [#form-factor]
| Characteristic | Specification |
| -------------------------- | -------------------------------- |
| Height | 1.2 m (3.94 ft) |
| Mass | 35 kg (77 lb) |
| Powered degrees of freedom | 25 |
| Legs | 6 DOF per leg |
| Arms | 5 DOF per arm |
| Body | 1 waist-yaw DOF |
| Head | 2 neck DOF: yaw and pitch |
| Structural materials | 7075 aluminum and MJF PA12 nylon |
Load Capability [#load-capability]
| Activity | Rated load | Peak load |
| ------------- | -----------: | ------------: |
| Squat | 5 kg | Not specified |
| Bicep curl | 5 kg per arm | 15 kg per arm |
| Front raise | 5 kg per arm | 15 kg per arm |
| Lateral raise | 6 kg per arm | 18 kg per arm |
Rated loads describe sustained operation for the published activity. Peak loads are short-duration limits rather than continuous operating targets.
Powered Joint Layout [#powered-joint-layout]
| Region | Joint axes | Powered joints |
| --------- | ------------------------------------------------------------- | -------------: |
| Each leg | Hip pitch, hip roll, hip yaw, knee, ankle pitch, ankle roll | 6 |
| Each arm | Shoulder pitch, shoulder roll, shoulder yaw, elbow, wrist yaw | 5 |
| Waist | Yaw | 1 |
| Neck | Yaw, pitch | 2 |
| **Total** | | **25** |
For locomotion, the two neck joints are locked to use a 23-DOF body model.
Joint Direction and Limits [#joint-direction-and-limits]
The signed values below use the robot model coordinate convention. Mirrored left and right joints can use opposite signs for the same physical movement.
The frame convention is blue for `z`, red for `x`, and green for `y`.
Actuator ID Mapping [#actuator-id-mapping]
An interactive visualization will be added so you can move every joint and test its limits. Until then, use the signed limits in the tables below.
Legs [#legs]
| Joint | Left range (rad) | Left axis | Right range (rad) | Right axis |
| ----------- | ---------------: | --------- | ----------------: | ---------- |
| Hip pitch | -2.09 to 1.00 | `0 1 0` | -1.00 to 2.09 | `0 -1 0` |
| Hip roll | -0.79 to 0.79 | `1 0 0` | -0.79 to 0.79 | `1 0 0` |
| Hip yaw | -0.79 to 0.79 | `0 0 -1` | -0.79 to 0.79 | `0 0 -1` |
| Knee | 0.00 to 1.50 | `0 1 0` | -1.50 to 0.00 | `0 -1 0` |
| Ankle pitch | -0.35 to 0.35 | `0 1 0` | -0.35 to 0.35 | `0 -1 0` |
| Ankle roll | -0.10 to 0.10 | `-1 0 0` | -0.10 to 0.10 | `-1 0 0` |
Arms [#arms]
| Joint | Left range (rad) | Left axis | Right range (rad) | Right axis |
| -------------- | ---------------: | --------------- | ----------------: | --------------- |
| Shoulder pitch | -3.14 to 0.87 | `0 1 0` | -0.87 to 3.14 | `0 -1 0` |
| Shoulder roll | -1.57 to 0.00 | `-1 0 0` | 0.00 to 1.57 | `-1 0 0` |
| Shoulder yaw | -1.57 to 1.57 | `0 0 -1` | -1.57 to 1.57 | `0 0 -1` |
| Elbow | 0.00 to 2.44 | `0 -1 0` | -2.44 to 0.00 | `0 1 0` |
| Wrist yaw | -3.14 to 3.14 | `0.766 0 -0.64` | -3.14 to 3.14 | `0.766 0 -0.64` |
Waist and Neck [#waist-and-neck]
| Joint | Range (rad) | Axis |
| ---------- | ------------: | ------- |
| Waist yaw | -1.57 to 1.57 | `0 0 1` |
| Neck yaw | -1.57 to 1.57 | `0 0 1` |
| Neck pitch | -0.79 to 0.79 | `0 1 0` |
Actuation and Mechanical Power [#actuation-and-mechanical-power]
| Joint roles | Actuator family | Rated torque | Peak torque |
| --------------------------- | ------------------ | -----------: | ----------: |
| Hip pitch and waist yaw | `EC-A6416-P2-25` | 40 Nm | 120 Nm |
| Hip roll and shoulder pitch | `EC-A5013-H17-100` | 30 Nm | 90 Nm |
| Hip yaw and shoulder yaw | `EC-A3814-H14-107` | 20 Nm | 60 Nm |
| Knee and shoulder roll | `EC-A4315-P2-36` | 25 Nm | 75 Nm |
| Elbow, wrist yaw, and neck | `EC-A4310-P2-36` | 12 Nm | 36 Nm |
Rated torque is the continuous actuator output under normal thermal conditions. Peak torque is available only for short events.
Full Actuator Specifications [#full-actuator-specifications]
The table below maps every physical actuator ID to its joint role and model parameters. The parallel ankles use physical actuators A and B, so their joint-space pitch and roll limits are defined by the ankle mechanism rather than by one actuator alone.
| ID | Physical role | Model | Motion range (rad) | Velocity limit (rad/s) | Kt | Armature | Rated torque (Nm) | Peak torque (Nm) | Static friction | Dynamic friction |
| -: | ---------------------------- | ------------------ | ------------------- | ---------------------: | ---: | --------: | ----------------: | ---------------: | --------------: | ---------------: |
| 1 | `left_hip_pitch_joint` | `EC-A6416-P2-25` | -2.09 to 1.00 | 12.57 | 2.75 | 0.095625 | 40.0 | 120.0 | 0.70 | 0.050 |
| 2 | `left_hip_roll_joint` | `EC-A5013-H17-100` | -0.79 to 0.79 | 3.98 | 7.15 | 0.11 | 30.0 | 90.0 | 0.20 | 0.020 |
| 3 | `left_hip_yaw_joint` | `EC-A3814-H14-107` | -0.79 to 0.79 | 5.45 | 5.70 | 0.038 | 20.0 | 60.0 | 0.70 | 0.050 |
| 4 | `left_knee_joint` | `EC-A4315-P2-36` | 0.00 to 1.50 | 12.25 | 2.53 | 0.0339552 | 25.0 | 75.0 | 0.70 | 0.020 |
| 5 | `left_ankle_A` | `EC-A4310-P2-36` | See ankle mechanism | 9.32 | 1.81 | 0.0565056 | 40.0 | 145.4 | 0.40 | 0.015 |
| 6 | `left_ankle_B` | `EC-A4310-P2-36` | See ankle mechanism | 9.32 | 1.81 | 0.0565056 | 17.0 | 57.6 | 0.40 | 0.015 |
| 7 | `right_hip_pitch_joint` | `EC-A6416-P2-25` | -1.00 to 2.09 | 12.57 | 2.75 | 0.095625 | 40.0 | 120.0 | 0.70 | 0.050 |
| 8 | `right_hip_roll_joint` | `EC-A5013-H17-100` | -0.79 to 0.79 | 3.98 | 7.15 | 0.11 | 30.0 | 90.0 | 0.20 | 0.020 |
| 9 | `right_hip_yaw_joint` | `EC-A3814-H14-107` | -0.79 to 0.79 | 5.45 | 5.70 | 0.038 | 20.0 | 60.0 | 0.70 | 0.050 |
| 10 | `right_knee_joint` | `EC-A4315-P2-36` | -1.50 to 0.00 | 12.25 | 2.53 | 0.0339552 | 25.0 | 75.0 | 0.70 | 0.020 |
| 11 | `right_ankle_A` | `EC-A4310-P2-36` | See ankle mechanism | 9.32 | 1.81 | 0.0565056 | 40.0 | 145.4 | 0.40 | 0.015 |
| 12 | `right_ankle_B` | `EC-A4310-P2-36` | See ankle mechanism | 9.32 | 1.81 | 0.0565056 | 17.0 | 57.6 | 0.40 | 0.015 |
| 13 | `left_shoulder_pitch_joint` | `EC-A5013-H17-100` | -3.14 to 0.87 | 3.98 | 7.15 | 0.11 | 30.0 | 90.0 | 0.20 | 0.020 |
| 14 | `left_shoulder_roll_joint` | `EC-A4315-P2-36` | -1.57 to 0.00 | 12.25 | 2.53 | 0.0339552 | 25.0 | 75.0 | 0.70 | 0.020 |
| 15 | `left_shoulder_yaw_joint` | `EC-A3814-H14-107` | -1.57 to 1.57 | 5.45 | 5.70 | 0.038 | 20.0 | 60.0 | 0.70 | 0.050 |
| 16 | `left_elbow_joint` | `EC-A4310-P2-36` | 0.00 to 2.44 | 9.32 | 1.81 | 0.0282528 | 12.0 | 36.0 | 0.40 | 0.015 |
| 17 | `left_wrist_yaw_joint` | `EC-A4310-P2-36` | -3.14 to 3.14 | 9.32 | 1.81 | 0.0282528 | 12.0 | 36.0 | 0.40 | 0.015 |
| 18 | `right_shoulder_pitch_joint` | `EC-A5013-H17-100` | -0.87 to 3.14 | 3.98 | 7.15 | 0.11 | 30.0 | 90.0 | 0.20 | 0.020 |
| 19 | `right_shoulder_roll_joint` | `EC-A4315-P2-36` | 0.00 to 1.57 | 12.25 | 2.53 | 0.0339552 | 25.0 | 75.0 | 0.70 | 0.020 |
| 20 | `right_shoulder_yaw_joint` | `EC-A3814-H14-107` | -1.57 to 1.57 | 5.45 | 5.70 | 0.038 | 20.0 | 60.0 | 0.70 | 0.050 |
| 21 | `right_elbow_joint` | `EC-A4310-P2-36` | -2.44 to 0.00 | 9.32 | 1.81 | 0.0282528 | 12.0 | 36.0 | 0.40 | 0.015 |
| 22 | `right_wrist_yaw_joint` | `EC-A4310-P2-36` | -3.14 to 3.14 | 9.32 | 1.81 | 0.0282528 | 12.0 | 36.0 | 0.40 | 0.015 |
| 23 | `waist_yaw_joint` | `EC-A6416-P2-25` | -1.57 to 1.57 | 12.57 | 2.75 | 0.095625 | 40.0 | 120.0 | 0.70 | 0.050 |
| 24 | `neck_yaw_joint` | `EC-A4310-P2-36` | -1.57 to 1.57 | 9.32 | 1.81 | 0.0282528 | 12.0 | 36.0 | 0.40 | 0.015 |
| 25 | `neck_pitch_joint` | `EC-A4310-P2-36` | -0.79 to 0.79 | 9.32 | 1.81 | 0.0282528 | 12.0 | 36.0 | 0.40 | 0.015 |
Parameter Definitions [#parameter-definitions]
* `Kt` is the actuator torque constant: `torque (Nm) = Kt * current (A)`.
* `Armature` represents reflected rotor inertia in joint space and contributes corresponding damping. For a geared actuator, `armature = JG^2`, where `J` is rotor inertia and `G` is the actuator-to-output angular-velocity ratio.
* `Rated torque` is the continuous output the actuator can sustain under normal thermal conditions.
* `Peak torque` is the short-duration maximum used for events such as impacts, push-off, or fast recovery.
* `Static friction` is the torque required to start the joint moving from rest.
* `Dynamic friction` is the lower resisting torque after the joint is moving.
Parallel Ankle [#parallel-ankle]
Asimov 1 uses a parallel RSU ankle (Revolute-Spherical-Universal) rather than a serial single-DOF mechanism. Two revolute actuators work through parallel linkages to produce ankle pitch and roll.
In simulation, ankle pitch and roll are modeled as directly actuated ideal joints. On the physical robot, the two actuators move endpoints on a straight bar, so a kinematic mapping converts actuator A/B positions into pitch/roll coordinates for Sim2Real deployment.
Asimov 1 no longer uses passive toe joints. The current design uses fixed flat feet.
The mapping uses:
* `theta_p`: ankle pitch angle in joint space
* `theta_r`: ankle roll angle in joint space
* `theta_A`, `theta_B`: actuator A and B rotation angles
* `r_A`, `r_B`: effective linkage radii for actuators A and B
* `r`: common radius when `r_A = r_B = r`
* `d`: ankle pivot to bar midpoint-line distance
* `c`: distance between the two bar endpoints
The simplified model assumes small angles, rigid symmetric geometry, constant effective radii, and no compliance, backlash, or slip. Positive `y` points away from the ground, and the right-leg convention is used for the derivation.
Mapping equations from actuator space to pitch/roll space:
```text
theta_p = (r_A * theta_A - r_B * theta_B) / (2 * d)
theta_p = (theta_A - theta_B) * (r / (2 * d))
theta_r = -(r_A * theta_A + r_B * theta_B) / c
theta_r = -(theta_A + theta_B) * (r / c)
```
```c
float right_pitch = (right_A - right_B) / (2.0f * K_PITCH);
float right_roll = -(right_A + right_B) / (2.0f * K_ROLL);
float left_pitch = (left_A - left_B) / (2.0f * K_PITCH);
float left_roll = -(left_A + left_B) / (2.0f * K_ROLL);
```
See the public [ankle mechanism derivation](https://github.com/asimovinc/asimov-v0/blob/main/mechanical/ankle_mechanism.md) for the complete derivation.
Further Reading [#further-reading]
* [Parallel ankle context](https://arxiv.org/pdf/2509.16469)
* [Unitree G1 basic motion reference](https://support.unitree.com/home/en/G1_developer/basic_motion_routine)
* [Omniverse articulation stability and armature](https://docs.omniverse.nvidia.com/kit/docs/omni_physics/107.3/dev_guide/guides/articulation_stability_guide.html#joint-armature)
# Software
Asimov 1 separates low-level motion control from media, networking, and application connectivity. This lets developers work at different layers without treating the robot as a single software process.
Compute Responsibilities [#compute-responsibilities]
| Layer | Runs on | Responsibility |
| -------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Motion control | Radxa CM5 in the RPU | Actuator communication, IMU state, control modes, command handling, and firmware telemetry |
| Edge services | Raspberry Pi 5 in the Head Compute Unit | Camera, microphone, speaker, networking, media transport, and external connectivity |
| Application | External computer or service | Operator interfaces, data processing, teleoperation logic, autonomy, and project-specific behavior |
The CM5 and Pi 5 use matched software images and communicate over a direct Ethernet link. The Pi 5 sits in the Head Compute Unit, and the CM5 sits in the Robot Processing Unit (RPU) in the torso.
Data Flow [#data-flow]
The documented low-level interface uses LiveKit and WebRTC to connect an application to the robot.
| Path | Direction | Content | Transport |
| ------------- | -------------------- | ----------------------------------------------------------- | -------------------- |
| Commands | Application to robot | Mode, velocity, and joint trajectory requests | Reliable DataChannel |
| Telemetry | Robot to application | Joint state, current, temperature, IMU, and alerts at 10 Hz | Lossy DataTrack |
| System events | Robot to application | Errors and diagnostics | Reliable DataChannel |
| Video | Robot to application | H.264 video, requested at 1280 x 720 and 30 fps | WebRTC media track |
| Audio | Bidirectional | Microphone input and speaker output | Opus media tracks |
The [Asimov API reference](/asimov/1/program/api) documents these release-specific control, telemetry, media, and protocol interfaces.
Control Model [#control-model]
The current interface describes three firmware control states:
| State | Purpose |
| ----- | ---------------------------------------------------------------- |
| DAMP | Software-requested compliant actuator state |
| STAND | Transition toward a standing pose |
| MOVE | Policy-based velocity control or direct joint trajectory control |
Direct trajectory messages contain targets for all 25 powered joints. Policy control accepts planar velocity commands, while telemetry reports the robot state back to the application.
DAMP is a software-requested compliant state. Use the released startup, support, shutdown, and emergency-disconnect procedures when operating the physical robot.
Included Locomotion [#included-locomotion]
The CM5 firmware image includes the Asimov 1 locomotion policy. After both computers are flashed and the robot completes first-time setup, no separate policy installation is required.
Velocity commands provide forward, backward, lateral, and turning control. The locomotion policy runs against the 23-DOF body model, with the two neck joints locked.
Direct Control and Teleoperation [#direct-control-and-teleoperation]
Trajectory requests provide direct control over all 25 actuator targets. An active trajectory session streams at approximately 50 Hz and automatically transitions to DAMP if packets stop.
Directional remote driving through velocity commands is available. The camera, microphone array, speaker, and bidirectional media tracks provide the audiovisual path for remote interaction. Full-body joint teleoperation is in development.
Python Client SDK and Emulator [#python-client-sdk-and-emulator]
We are actively developing and battle-testing the Python Client SDK and emulator internally, with a public release coming soon.
# Overview
Asimov 1 exposes an API for directional driving, direct actuator control, 10 Hz telemetry, and media streaming through [LiveKit](https://livekit.io) WebRTC. The locomotion policy is included in the supplied Radxa CM5 firmware for the Robot Processing Unit (RPU).
Use this low-level reference only with the generated protobuf bindings supplied by the supported Asimov software release on the robot. Do not infer field compatibility across releases. API commands are not a commissioning procedure, and a software DAMP command does not replace the independent hardware emergency-stop system.
Architecture [#architecture]
**Data flow**: Your application connects to a LiveKit room. The robot publishes video and telemetry. You send commands back. Everything travels over WebRTC - low latency, NAT traversal, adaptive bitrate.
Command Processing [#command-processing]
Your commands don't reach the actuators directly - the robot validates, transforms, and safety-checks every command before acting on it. It remaps joints, injects PD gains, gates commands when in a safety state, and auto-DAMPs if your application disconnects. See [Robot Control](/asimov/1/program/api/robot-control) for details.
**Two DataChannels**, **one DataTrack**, plus media tracks carry traffic between your application and the robot:
| Name | Direction | Content | Delivery |
| ----------- | ------------- | --------------------------------- | --------------------- |
| `commands` | You -> Robot | Control commands | DataChannel, reliable |
| `telemetry` | Robot -> You | Joint state, IMU, alerts at 10 Hz | DataTrack, lossy |
| `system` | Robot -> You | Errors, diagnostics | DataChannel, reliable |
| Video | Robot -> You | H.264, 30 fps | LiveKit video track |
| Audio | bidirectional | Opus, mic up + speaker down | LiveKit audio track |
State Machine [#state-machine]
The robot has 3 control modes (DAMP, STAND, MOVE) and three ways to drive it:
* **A. Walk** - `ModeCommand(STAND)` -> wait \~2 s -> `VelocityCommand`. Robot stands, then the locomotion policy walks at the commanded velocity.
* **B. Joint teleop** - `ModeCommand(STAND)` -> wait \~2 s -> `TrajectoryRequest` @ \~50 Hz. Robot stands first, then accepts direct joint targets from a settled pose.
* **C. Direct joint control** - `TrajectoryRequest` straight from DAMP, no `ModeCommand` needed. Direct PD engages immediately. The robot transitions from compliant (limp) to actively driven on the first packet - your first target should be a sensible pose.
The STAND ramp takes \~2 s but is **advisory**, not enforced - the firmware accepts MOVE commands the moment they arrive. Clients that want a settled pose before walking or teleoperating must wait themselves.
Prerequisites [#prerequisites]
Before connecting to the robot, you need:
. **LiveKit server** - Self-host (see below) or use [LiveKit Cloud](https://livekit.io).
. **Python LiveKit SDK** - Install with `pip install livekit livekit-api`.
. **Asimov protobuf bindings** - Use the generated Python bindings supplied with your supported Asimov software release. The examples below import them from `asimov_protocol.v1.edge_cloud_pb2`.
For a typed Python client, use the [Python SDK](/asimov/1/program/sdk), which speaks this wire over UDP on the local network or over the LiveKit room, and adds the keepalive, waits and error model on top. This page is the low-level interface underneath it. Menlo Platform is coming soon and is not required for this API.
Self-Hosting LiveKit [#self-hosting-livekit]
Install the LiveKit server:
* **macOS:** `brew install livekit`
* **Linux:** `curl -sSL https://get.livekit.io | bash`
Start in development mode:
```bash
LIVEKIT_ENABLE_DATA_TRACKS=true livekit-server --dev --bind 0.0.0.0
```
This runs a server at `ws://localhost:7880` with dev credentials (`devkey` / `secret`).
`LIVEKIT_ENABLE_DATA_TRACKS=true` is **required** - without it, the robot's telemetry DataTrack will fail to publish.
Generate an Access Token [#generate-an-access-token]
```bash
livekit-cli create-token \
--api-key devkey --api-secret secret \
--join --room robot --identity my-client \
--valid-for 24h
```
For full LiveKit documentation, see [docs.livekit.io](https://docs.livekit.io).
Quick Start [#quick-start]
```python
import asyncio
import time
from livekit import rtc
from asimov_protocol.v1.edge_cloud_pb2 import (
CloudCommand, VelocityCommand, ModeCommand, Mode, EdgeTelemetry
)
LIVEKIT_URL = "ws://localhost:7880"
LIVEKIT_TOKEN = "your-token"
seq = 0
def next_seq():
global seq
seq += 1
return seq
async def main():
room = rtc.Room()
await room.connect(LIVEKIT_URL, LIVEKIT_TOKEN)
print(f"Connected to room: {room.name}")
# Subscribe to robot's video track
@room.on("track_subscribed")
def on_track(track, publication, participant):
if isinstance(track, rtc.RemoteVideoTrack):
pass # handle video frames
# Subscribe to robot's telemetry DataTrack
async def read_telemetry(track):
stream = track.subscribe()
async for frame in stream:
t = EdgeTelemetry.FromString(frame.payload)
print(f"Mode: {t.fw_mode}, Joints: {list(t.joint_pos)[:5]}...")
@room.on("data_track_published")
def on_data_track(track):
asyncio.create_task(read_telemetry(track))
# Stand up first (robot boots in DAMP).
# Note: command Mode and telemetry FirmwareMode use different numbering -
# always use the named constants instead of raw ints.
stand = CloudCommand(
timestamp_us=int(time.time() * 1e6),
sequence=next_seq(),
mode=ModeCommand(mode=Mode.MODE_STAND)
)
await room.local_participant.publish_data(
stand.SerializeToString(), topic="commands", reliable=True
)
await asyncio.sleep(3) # wait for standing pose to settle
# Walk forward for 5 seconds at 10 Hz.
# Velocity commands persist - once set, the robot keeps walking at the
# last commanded velocity until you change it or send DAMP. Streaming at
# 10 Hz here is just to demonstrate updates, not a safety requirement.
for _ in range(50):
cmd = CloudCommand(
timestamp_us=int(time.time() * 1e6),
sequence=next_seq(),
velocity=VelocityCommand(vx=0.5, vy=0, vyaw=0)
)
await room.local_participant.publish_data(
cmd.SerializeToString(), topic="commands", reliable=True
)
await asyncio.sleep(0.1)
# Stop - send DAMP
stop = CloudCommand(
timestamp_us=int(time.time() * 1e6),
sequence=next_seq(),
mode=ModeCommand(mode=Mode.MODE_DAMP)
)
await room.local_participant.publish_data(
stop.SerializeToString(), topic="commands", reliable=True
)
if __name__ == "__main__":
asyncio.run(main())
```
Sections [#sections]
* [Robot Control](/asimov/1/program/api/robot-control) - commands, state machine, telemetry, joint order
* [Media](/asimov/1/program/api/media) - camera and video streaming
* [Protocols](/asimov/1/program/api/protocols) - data structures, field limits, examples
* [Wire Format](/asimov/1/program/api/wire-format) - session channels and message shapes
# Media
The Asimov robot streams video and audio in real-time via WebRTC.
Camera [#camera]
| Spec | Value |
| ------------ | ------------------------------------------------- |
| Capture rate | 30 fps |
| Resolution | 1280x720 (requested - actual depends on hardware) |
| Encoding | H.264 |
| Streaming | LiveKit video track (WebRTC) |
The robot captures frames from its USB camera at 30fps and publishes them as a LiveKit video track. Any participant in the LiveKit room can subscribe to the track to receive the stream.
The camera must be plugged in before the robot starts - the handle is grabbed once on boot.
Subscribing to Video [#subscribing-to-video]
```python
import asyncio
from livekit import rtc
async def main():
room = rtc.Room()
await room.connect(livekit_url, token)
@room.on("track_subscribed")
def on_track(track, publication, participant):
if track.kind == rtc.TrackKind.KIND_VIDEO:
# process video frames
pass
# Keep connection alive
await asyncio.sleep(10)
if __name__ == "__main__":
asyncio.run(main())
```
Audio [#audio]
The robot supports bidirectional audio over LiveKit.
| Direction | Spec | Description |
| ------------ | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Robot -> You | native device rate, 16-bit, mono | Microphone capture from the robot. Rate is auto-detected (e.g. 16 kHz on ReSpeaker XVF3800). LiveKit resamples on the client - request any rate via `AudioStream(sample_rate=...)`. |
| You -> Robot | 48 kHz, 16-bit, mono | Played through the robot's speaker |
Subscribing to Robot Audio [#subscribing-to-robot-audio]
```python
import asyncio
from livekit import rtc
async def handle_audio(track):
stream = rtc.AudioStream(track, sample_rate=48000, num_channels=1)
async for event in stream:
pcm_data = bytes(event.frame.data) # int16 PCM
# process audio frames
await stream.aclose()
async def main():
room = rtc.Room()
await room.connect(livekit_url, token)
@room.on("track_subscribed")
def on_track(track, publication, participant):
if track.kind == rtc.TrackKind.KIND_AUDIO:
asyncio.ensure_future(handle_audio(track))
await asyncio.sleep(10)
if __name__ == "__main__":
asyncio.run(main())
```
Sending Audio to the Robot [#sending-audio-to-the-robot]
Publish an audio track to the LiveKit room. The robot subscribes and plays it through its speaker.
```python
import asyncio
from livekit import rtc
async def main():
room = rtc.Room()
await room.connect(livekit_url, token)
source = rtc.AudioSource(sample_rate=48000, num_channels=1)
track = rtc.LocalAudioTrack.create_audio_track("my-audio", source)
await room.local_participant.publish_track(
track,
rtc.TrackPublishOptions(source=rtc.TrackSource.SOURCE_MICROPHONE),
)
# Push 10ms frames of int16 PCM
frame = rtc.AudioFrame.create(sample_rate=48000, num_channels=1, samples_per_channel=480)
# fill frame.data with your audio samples
await source.capture_frame(frame)
await asyncio.sleep(10)
if __name__ == "__main__":
asyncio.run(main())
```
See the [Asimov API overview](/asimov/1/program/api) for connection setup.
# Protocols
All commands and telemetry use Protocol Buffers, sent over LiveKit DataChannels and DataTracks.
The field definitions on this page must be used with the generated bindings from the matching supported Asimov software release.
Commands [#commands]
Commands are sent from your application to the robot on the `commands` data channel.
CloudCommand [#cloudcommand]
Every command is wrapped in a `CloudCommand` envelope with a timestamp and sequence number.
| Field | Type | Description |
| -------------- | ----------------- | ----------------------------- |
| `timestamp_us` | uint64 | Timestamp in microseconds |
| `sequence` | uint32 | Monotonic sequence number |
| `velocity` | VelocityCommand | Walk command (one of) |
| `trajectory` | TrajectoryRequest | Direct joint control (one of) |
| `mode` | ModeCommand | Mode switch (one of) |
Only one of `velocity`, `trajectory`, or `mode` should be set per command.
***
VelocityCommand [#velocitycommand]
Make the robot walk with the given velocity. Automatically enters MOVE/POLICY mode.
| Field | Type | Envelope | Unit | Description |
| ------ | ----- | ----------- | ----- | ------------------------------------ |
| `vx` | float | -1.0 to 1.0 | m/s | Forward (+) / backward (-) velocity |
| `vy` | float | -1.0 to 1.0 | m/s | Strafe left (+) / right (-) velocity |
| `vyaw` | float | -1.5 to 1.5 | rad/s | Turn left (+) / right (-) rate |
The edge does not clamp these values - it rejects non-finite input and forwards the rest. See [Robot Control](/asimov/1/program/api/robot-control) for the full envelope note.
Example (Python) [#example-python]
```python
import asyncio
import time
from livekit import rtc
from asimov_protocol.v1.edge_cloud_pb2 import CloudCommand, VelocityCommand
async def main():
room = rtc.Room()
await room.connect(livekit_url, token)
# Send velocity command
cmd = CloudCommand(
timestamp_us=int(time.time() * 1e6),
sequence=1,
velocity=VelocityCommand(vx=0.3, vy=0.0, vyaw=0.0)
)
await room.local_participant.publish_data(
cmd.SerializeToString(), topic="commands", reliable=True
)
await asyncio.sleep(1)
if __name__ == "__main__":
asyncio.run(main())
```
***
TrajectoryRequest [#trajectoryrequest]
Command a direct joint trajectory. Positions use **firmware order** (25 joints) - see [Joint Order](/asimov/1/program/api/robot-control#joint-order).
| Field | Type | Description |
| ------ | -------------- | ----------------------- |
| `full` | FullTrajectory | Custom joint trajectory |
FullTrajectory [#fulltrajectory]
A sequence of joint position targets with timing.
| Field | Type | Description |
| ---------- | --------------- | ---------------------------- |
| `segments` | JointSegment\[] | Ordered list of target poses |
JointSegment [#jointsegment]
| Field | Count | Range | Unit | Description |
| ----------- | ----- | ----------------- | ------- | --------------------------------------------------------------------------------------------- |
| `positions` | 25 | actuator-specific | radians | Target joint positions in firmware order. Every entry drives its actuator - no skip sentinel. |
| `kp` | 25 | 0 - 500 | - | Position gain per joint (optional - edge fills defaults if omitted) |
| `kd` | 25 | 0 - 5.0 | - | Velocity damping gain per joint (optional - edge fills defaults if omitted) |
Each `JointSegment` is applied as an instantaneous PD target. There is no in-firmware interpolation between segments - clients are responsible for any smoothing.
***
ModeCommand [#modecommand]
Switch the robot's control mode.
| Field | Type | Values | Description |
| ------ | --------- | --------------------------- | ----------- |
| `mode` | Mode enum | MODE\_STAND=0, MODE\_DAMP=1 | Target mode |
> **Watch out:** The command enum (`Mode`) and the telemetry enum (`FirmwareMode`) use **different numbering** for the same modes:
>
> | | DAMP | STAND |
> | -------------------------- | ---- | ----- |
> | Commands (`Mode`) | 1 | 0 |
> | Telemetry (`FirmwareMode`) | 0 | 1 |
>
> Don't compare them numerically - use the named constants.
See [Robot Control](/asimov/1/program/api/robot-control) for mode transitions.
***
Telemetry [#telemetry]
Sent from the robot at 10 Hz on the `telemetry` DataTrack (lossy - dropped packets are acceptable since the next update arrives in 100ms).
| Field | Type | Count | Description |
| ------------------------- | ------------- | -------- | ----------------------------------------------------- |
| `timestamp_us` | uint64 | 1 | Edge clock (microseconds) |
| `fw_timestamp_us` | uint64 | 1 | Firmware clock (microseconds) |
| `sequence` | uint32 | 1 | Monotonic counter |
| `fw_mode` | FirmwareMode | 1 | FW\_MODE\_DAMP=0, FW\_MODE\_STAND=1, FW\_MODE\_MOVE=2 |
| `joint_pos` | float | 25 | Joint positions (radians) |
| `joint_vel` | float | 25 | Joint velocities (rad/s) |
| `joint_current` | float | 25 | Actuator current (amps) |
| `joint_temp` | float | 25 | Actuator temperature (celsius) |
| `imu_quat` | float | 4 | Orientation quaternion \[w, x, y, z] |
| `imu_gyro` | float | 3 | Angular velocity \[x, y, z] (rad/s) |
| `imu_gravity` | float | 3 | Projected gravity vector \[x, y, z] |
| `error_flags` | uint32 | 1 | Active errors bitfield |
| `active_alerts` | FirmwareAlert | repeated | Current hardware alerts - see below |
| `fw_age_ms` | uint32 | 1 | Staleness of firmware data when forwarded (ms) |
| `last_video_timestamp_us` | uint64 | 1 | Most recent video frame timestamp (for media sync) |
| `last_audio_timestamp_us` | uint64 | 1 | Most recent audio frame timestamp (for media sync) |
FirmwareAlert [#firmwarealert]
Hardware alerts forwarded from the firmware safety layer. Present in `EdgeTelemetry.active_alerts` whenever an alert condition is active. Clears automatically when the condition resolves (e.g. actuator cools below threshold).
| Field | Type | Description |
| -------------- | ------ | ----------------------------------------- |
| `id` | uint32 | Alert type identifier |
| `severity` | uint32 | Severity level (see table below) |
| `value` | uint32 | Measured value that triggered the alert |
| `threshold` | uint32 | Threshold that was exceeded |
| `first_set_us` | uint64 | Firmware timestamp when alert first fired |
| `source_id` | uint32 | Source (e.g. actuator index) |
**Severity values:**
| Value | Meaning |
| ----- | -------- |
| 0 | Critical |
| 1 | Warning |
| 2 | Info |
Common alerts: actuator overtemperature (trips at 80 C, clears at 70 C), fall detection, CAN bus faults.
Example (Python) [#example-python-1]
```python
import asyncio
from livekit import rtc
from asimov_protocol.v1.edge_cloud_pb2 import EdgeTelemetry
async def read_telemetry(track):
stream = track.subscribe()
async for frame in stream:
t = EdgeTelemetry.FromString(frame.payload)
print(f"Mode: {t.fw_mode}")
print(f"Joint positions: {list(t.joint_pos)}")
print(f"IMU quaternion: {list(t.imu_quat)}")
async def main():
room = rtc.Room()
await room.connect(livekit_url, token)
# Telemetry arrives on a DataTrack, not a DataChannel
@room.on("data_track_published")
def on_data_track(track):
asyncio.create_task(read_telemetry(track))
await asyncio.sleep(10)
if __name__ == "__main__":
asyncio.run(main())
```
***
System Events [#system-events]
Sent from the robot on the `system` data channel (reliable - these must not be lost).
EdgeEvent [#edgeevent]
| Field | Type | Description |
| -------------- | --------------- | -------------------------------------------------- |
| `timestamp_us` | uint64 | Timestamp in microseconds |
| `sequence` | uint32 | Monotonic counter |
| `error` | EdgeError | Error report (one of) - emitted on error |
| `diagnostics` | EdgeDiagnostics | Status snapshot (one of) - emitted every \~1s |
| `controller` | ControllerEvent | Control source change (one of) - emitted on change |
EdgeError [#edgeerror]
| Field | Type | Description |
| ----------- | --------- | ------------------------------------------------ |
| `subsystem` | Subsystem | Source subsystem (see enum below) |
| `code` | string | e.g. "CAMERA\_OPEN\_FAILED", "FW\_LINK\_TIMEOUT" |
| `message` | string | Human-readable detail |
**Subsystem enum**: `SUBSYSTEM_FW_LINK=0`, `SUBSYSTEM_CAMERA=1`, `SUBSYSTEM_MIC=2`, `SUBSYSTEM_SPEAKER=3`, `SUBSYSTEM_BLE=4`, `SUBSYSTEM_CLOUD=5`, `SUBSYSTEM_FIRMWARE=6`
EdgeDiagnostics [#edgediagnostics]
Emitted every \~1 second on the `system` channel. Provides a snapshot of edge and firmware health.
| Field | Type | Description |
| ----------------- | --------------- | --------------------------------------------------------------- |
| `can_health` | CanBusHealth\[] | Per-bus CAN statistics (frames sent/received, errors, bus-offs) |
| `onnx_avg_ms` | float | Average ONNX inference time (ms) |
| `onnx_max_ms` | float | Max ONNX inference time (ms) |
| `onnx_count` | uint64 | Total inference count |
| `controller` | string | Active controller: "ble", "cloud", or "" |
| `provisioned` | bool | Robot setup complete |
| `cloud_connected` | bool | LiveKit connection active |
| `ble_connected` | bool | Phone connected via BLE |
| `camera_active` | bool | Camera capturing |
| `mic_active` | bool | Microphone active |
ControllerEvent [#controllerevent]
Fired when the active control source changes.
| Field | Type | Description |
| ---------- | ------ | ----------------------------------------------- |
| `previous` | string | Previous controller |
| `current` | string | New controller |
| `reason` | string | "acquired", "override", "timeout", "disconnect" |
# Robot Control
Send commands on the `commands` DataChannel, receive telemetry on the `telemetry` DataTrack. See the [Asimov API overview](/asimov/1/program/api) for connection setup.
Use these commands only on a commissioned robot with version-matched software, validated support and operating procedures, and an independent hardware emergency stop. DAMP is a software control state, not an emergency-stop system.
Control Modes [#control-modes]
See the [state machine diagram](/asimov/1/program/api) on the overview page for a visual reference.
| Mode | Description |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **DAMP** | Actuators go compliant (limp). Safe default. The robot enters DAMP automatically on: boot, fall detected, actuator overtemp, trajectory session timeout (2 s), or explicit `ModeCommand(DAMP)`. |
| **STAND** | Robot ramps to a standing pose over \~2 s. The ramp is **advisory** - the firmware accepts MOVE commands the moment they arrive. If you want a settled pose before commanding MOVE, wait \~2 s after sending `ModeCommand(STAND)`. |
| **MOVE/POLICY** | Neural network locomotion at 50 Hz. Send `VelocityCommand(vx, vy, vyaw)` to control walking speed and direction. |
| **MOVE/TRAJECTORY** | Direct PD control of joints. No neural network. Requires **continuous streaming** (\~50 Hz) - if trajectory packets stop for 2 s during an active session, the robot auto-DAMPs. Every value in `positions[25]` actively drives its actuator - there is no skip sentinel. For joints you don't want to actively command, send a hold target of your choice (e.g. the joint's current value read from `EdgeTelemetry.joint_pos`). |
Allowed Transitions [#allowed-transitions]
| From | To | Command | Notes |
| ----- | --------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Any | DAMP | `ModeCommand(DAMP)` | Always allowed as a software stop request; not a hardware emergency stop. |
| DAMP | STAND | `ModeCommand(STAND)` | Robot ramps to standing pose over \~2 s. Ramp is advisory - wait \~2 s before sending MOVE if you want a settled pose. |
| STAND | MOVE/POLICY | `VelocityCommand` | Sending velocity auto-enters POLICY. |
| STAND | MOVE/TRAJECTORY | `TrajectoryRequest` | Sending trajectory auto-enters TRAJECTORY. |
| MOVE | STAND | `ModeCommand(STAND)` | Returns to default standing pose. |
> **There is no DAMP -> MOVE shortcut.** While the firmware reports `FW_MODE_DAMP`, both `VelocityCommand` and `TrajectoryRequest` are dropped at the edge. Pass through `ModeCommand(STAND)` first, for velocity and trajectory alike.
**Trajectory session timeout**: while a trajectory teleop session is active, your application must stream `TrajectoryRequest` packets at \~50 Hz. If no trajectory packet arrives for **2 s**, the robot auto-DAMPs. This catches client disconnects (tab close, WiFi drop, grip release).
**Velocity staleness**: velocity has its own watchdog. If **2 s** pass with no new nonzero `VelocityCommand`, the edge issues a zero velocity - the robot holds in MOVE at a standstill rather than DAMPing or returning to STAND. Clients that deduplicate a held control must re-send the held nonzero command periodically (the reference clients use a 500 ms keepalive) or the robot will stop under them.
On the cloud path this watchdog is suppressed while the operator is still confirmed present in the session. The moment presence cannot be confirmed, the 2 s rule applies.
***
Commands [#commands]
Velocity (Walk) [#velocity-walk]
```python
import itertools
import time
from asimov_protocol.v1.edge_cloud_pb2 import CloudCommand, VelocityCommand
# Monotonic sequence counter shared by all examples on this page.
_seq = itertools.count(1)
def seq() -> int:
return next(_seq)
cmd = CloudCommand(
timestamp_us=int(time.time() * 1e6),
sequence=seq(),
velocity=VelocityCommand(vx=0.5, vy=0.0, vyaw=0.0)
)
await room.local_participant.publish_data(
cmd.SerializeToString(), topic="commands", reliable=True
)
```
| Field | Envelope | Unit | Description |
| ------ | ----------- | ----- | --------------------------- |
| `vx` | -1.0 to 1.0 | m/s | Forward (+) / backward (-) |
| `vy` | -1.0 to 1.0 | m/s | Strafe left (+) / right (-) |
| `vyaw` | -1.5 to 1.5 | rad/s | Turn left (+) / right (-) |
> **The edge does not clamp velocity.** It rejects non-finite values and forwards everything else verbatim; the firmware applies its own limits. The envelope above is the ceiling the reference operator clients pin every command to before sending, and it is the envelope this documentation is written against. Clamp on your side - do not rely on the robot to catch a runaway command.
> **Send a `ModeCommand(STAND)` first.** `VelocityCommand` packets received while the robot is in `FW_MODE_DAMP` are silently dropped - no error event is emitted. Wait until telemetry reports `FW_MODE_STAND` (or `FW_MODE_MOVE`) before sending velocity.
Trajectory (Direct Joint Control) [#trajectory-direct-joint-control]
Send 25 positions in firmware order. Every value actively drives its actuator - there is no skip sentinel. For joints you don't want to actively move, send a hold target of your choice (typical pattern: read `EdgeTelemetry.joint_pos` and override only the joints you're commanding).
Each `TrajectoryRequest` is applied as an instantaneous PD target - there is no in-firmware interpolation between segments. Clients are responsible for any smoothing across packets.
```python
import time
from asimov_protocol.v1.edge_cloud_pb2 import (
CloudCommand, TrajectoryRequest, FullTrajectory, JointSegment, EdgeTelemetry
)
# Keep a reference to the latest telemetry frame; update it from your
# telemetry subscription handler (see "Telemetry" below).
latest_telemetry: EdgeTelemetry | None = None
# ... once `latest_telemetry` has been populated by your subscription handler:
assert latest_telemetry is not None, "wait for the first telemetry frame"
# Seed positions from the latest telemetry pose so unspecified joints
# hold their current value; override only the ones you're driving.
positions = list(latest_telemetry.joint_pos) # 25 floats
positions[12] = 0.5 # L_Shoulder_Pitch
positions[13] = -0.2 # L_Shoulder_Roll
positions[15] = 1.2 # L_Elbow
cmd = CloudCommand(
timestamp_us=int(time.time() * 1e6),
sequence=seq(),
trajectory=TrajectoryRequest(
full=FullTrajectory(segments=[
JointSegment(positions=positions)
])
)
)
await room.local_participant.publish_data(
cmd.SerializeToString(), topic="commands", reliable=True
)
```
| Field | Count | Range | Unit | Description |
| ----------- | ----- | ----------------- | ------- | --------------------------------------------------------------------------------------- |
| `positions` | 25 | actuator-specific | radians | Target positions in firmware order. Every entry drives its actuator - no skip sentinel. |
| `kp` | 25 | 0 - 500 | - | Position gain (optional - edge fills defaults if omitted) |
| `kd` | 25 | 0 - 5.0 | - | Velocity damping gain (optional - edge fills defaults if omitted) |
> **KP/KD overrides are all-or-nothing.** To override gains, send exactly **25** values in `kp` and/or `kd`. Any other count (including partial overrides like 23 or 24 values) is silently replaced with the edge defaults.
Mode Switch [#mode-switch]
```python
import time
from asimov_protocol.v1.edge_cloud_pb2 import CloudCommand, ModeCommand, Mode
# Stand up
cmd = CloudCommand(
timestamp_us=int(time.time() * 1e6),
sequence=seq(),
mode=ModeCommand(mode=Mode.MODE_STAND)
)
# Request software DAMP (not a hardware emergency stop)
cmd = CloudCommand(
timestamp_us=int(time.time() * 1e6),
sequence=seq(),
mode=ModeCommand(mode=Mode.MODE_DAMP)
)
```
***
Command Validation [#command-validation]
The robot silently drops malformed or safety-gated commands - **no `EdgeError` event is emitted on the `system` channel** when this happens. Validate inputs client-side before sending. The current drop conditions:
| Command | Dropped when... |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VelocityCommand` | any of `vx`, `vy`, `vyaw` is non-finite (NaN/Inf), **or** firmware is in `FW_MODE_DAMP` |
| `TrajectoryRequest` | `JointSegment.positions` length != 25, **or** any position is non-finite, **or** `FullTrajectory.segments` is empty, **or** the request uses the unimplemented `id` source (LUT name) instead of `full`, **or** firmware is in `FW_MODE_DAMP` |
| `ModeCommand(STAND)` | firmware is **fault-DAMPed** - it entered DAMP by itself with `error_flags` set or a critical alert active. The robot will not stand back up into a fault. |
| `ModeCommand(DAMP)` | never dropped - always accepted |
Any command is also dropped if it arrives from a source that is not the arbiter's active controller.
Velocity values are **not** clamped by the edge - only non-finite values are rejected. KP/KD arrays of any length other than 25 are not rejected - edge silently substitutes its own defaults.
> **A failed STAND is not always a connectivity problem.** Check `error_flags` and `active_alerts` before retrying. Some firmware faults - a detected fall in particular - latch and clear only on a firmware restart; no command recovers them, and the locomotion policy has no get-up behaviour.
***
Telemetry [#telemetry]
The robot streams telemetry at 10 Hz on the `telemetry` DataTrack (lossy).
| Field | Type | Count | Description |
| ------------------------- | ------------- | -------- | ------------------------------------------------------------------------------- |
| `timestamp_us` | uint64 | 1 | Edge clock (microseconds) |
| `fw_timestamp_us` | uint64 | 1 | Firmware clock (microseconds) - use with `timestamp_us` for latency measurement |
| `sequence` | uint32 | 1 | Monotonic counter |
| `fw_mode` | FirmwareMode | 1 | FW\_MODE\_DAMP=0, FW\_MODE\_STAND=1, FW\_MODE\_MOVE=2 |
| `joint_pos` | float | 25 | Joint positions (radians) |
| `joint_vel` | float | 25 | Joint velocities (rad/s) |
| `joint_current` | float | 25 | Actuator current (amps) |
| `joint_temp` | float | 25 | Actuator temperature (celsius) |
| `imu_quat` | float | 4 | Orientation \[w, x, y, z] |
| `imu_gyro` | float | 3 | Angular velocity (rad/s) |
| `imu_gravity` | float | 3 | Projected gravity vector |
| `error_flags` | uint32 | 1 | Active errors bitfield |
| `active_alerts` | FirmwareAlert | repeated | Current hardware alerts (actuator overtemp, fall detect, CAN faults) |
| `fw_age_ms` | uint32 | 1 | How stale the firmware data was when forwarded (milliseconds) |
| `last_video_timestamp_us` | uint64 | 1 | Timestamp of the last video frame sent (for media sync) |
| `last_audio_timestamp_us` | uint64 | 1 | Timestamp of the last audio frame sent (for media sync) |
```python
import asyncio
from asimov_protocol.v1.edge_cloud_pb2 import EdgeTelemetry
async def read_telemetry(track):
stream = track.subscribe()
async for frame in stream:
t = EdgeTelemetry.FromString(frame.payload)
print(f"Mode: {t.fw_mode}, Joints: {list(t.joint_pos)}")
# Subscribe to the robot's telemetry DataTrack
@room.on("data_track_published")
def on_data_track(track):
asyncio.create_task(read_telemetry(track))
```
***
Joint Order [#joint-order]
All trajectory commands use **firmware order** - 25 joints matching the CAN bus actuator layout. These are the indices for the `positions` array.
| Group | Indices | Joints |
| ------------ | ------- | -------------------------------------------------------------------------------- |
| Left Leg | 0-5 | L\_Hip\_Pitch, L\_Hip\_Roll, L\_Hip\_Yaw, L\_Knee, L\_Ankle\_A, L\_Ankle\_B |
| Right Leg | 6-11 | R\_Hip\_Pitch, R\_Hip\_Roll, R\_Hip\_Yaw, R\_Knee, R\_Ankle\_A, R\_Ankle\_B |
| Left Arm | 12-16 | L\_Shoulder\_Pitch, L\_Shoulder\_Roll, L\_Shoulder\_Yaw, L\_Elbow, L\_Wrist\_Yaw |
| Right Arm | 17-21 | R\_Shoulder\_Pitch, R\_Shoulder\_Roll, R\_Shoulder\_Yaw, R\_Elbow, R\_Wrist\_Yaw |
| Waist + Neck | 22-24 | Waist\_Yaw, Neck\_Yaw, Neck\_Pitch |
PD Gains [#pd-gains]
KP/KD gains are injected automatically if you don't provide them. To override, send exactly 25 values in `kp` and/or `kd` matching the joint order above. For reference:
| Parameter | Range | Typical |
| ------------------ | ------- | ------- |
| Kp (position gain) | 0 - 500 | 40-150 |
| Kd (velocity gain) | 0 - 5.0 | 2.0-5.0 |
# Wire format
This page is a lookup for the messages exchanged between an application and Asimov 1. For value ranges, joint order, PD gains, and worked Python examples, see [Protocols](/asimov/1/program/api/protocols).
Session channels [#session-channels]
A session is a WebRTC room (implemented via LiveKit). Inside it are three data channels plus two media tracks:
| Channel | Reliability | Direction | Purpose |
| ----------- | ----------------- | ------------------- | -------------------------------------------------------------------------------- |
| `commands` | Reliable, ordered | Application → Robot | Send `CloudCommand` — a `oneof` of `velocity`, `trajectory`, or `mode` |
| `telemetry` | Lossy | Robot → Application | Receive `EdgeTelemetry` at 10 Hz |
| `system` | Reliable, ordered | Robot → Application | Receive `EdgeEvent` — robot-side events (boot, errors, alerts, mode transitions) |
| Video track | — | Robot → Cloud | FPV and third-person camera streams |
| Audio track | — | Bidirectional | Robot microphone and speaker |
Applications join the robot's LiveKit room and exchange these messages using the generated bindings supplied with the supported software release.
EdgeTelemetry [#edgetelemetry]
Delivered over the lossy `state` channel as protobuf. Each packet contains:
| Field | Description |
| ------------------------- | --------------------------------------------------------------------------------------------- |
| `timestamp_us` | Edge clock timestamp in µs (`CLOCK_REALTIME`) |
| `fw_timestamp_us` | Firmware clock timestamp in µs |
| `sequence` | Monotonically increasing packet counter |
| `fw_mode` | Current firmware mode (`FW_MODE_DAMP` / `FW_MODE_STAND` / `FW_MODE_MOVE`) |
| `joint_pos` | Joint positions in radians (25-element array, one per actuator) |
| `joint_vel` | Joint velocities in rad/s |
| `joint_current` | Actuator currents in amps |
| `joint_temp` | Actuator temperatures in °C |
| `imu_quat` | IMU orientation quaternion `[w, x, y, z]` |
| `imu_gyro` | IMU angular velocity `[x, y, z]` in rad/s |
| `imu_gravity` | Projected gravity vector from the IMU `[x, y, z]` |
| `error_flags` | Bitfield of active error codes |
| `alert_count` | Number of active alerts |
| `active_alerts` | Repeated `FirmwareAlert` — the alerts themselves (actuator overtemp, fall detect, CAN faults) |
| `fw_age_ms` | How stale the firmware data was when the edge forwarded it (ms) |
| `last_video_timestamp_us` | Timestamp of the most recent video frame (backend correlates with LiveKit RTP) |
| `last_audio_timestamp_us` | Timestamp of the most recent audio frame |
Use the generated `EdgeTelemetry` binding to decode each packet.
CloudCommand [#cloudcommand]
Sent on the reliable `commands` channel. `CloudCommand` is a protobuf `oneof` — exactly one of the three fields is set per message:
| Variant | Purpose |
| ------------ | --------------------------------------------------------------------------------------------------- |
| `velocity` | `VelocityCommand` with `vx`, `vy`, `vyaw` |
| `trajectory` | Joint-space trajectory — see [TrajectoryRequest](/asimov/1/program/api/protocols#trajectoryrequest) |
| `mode` | Firmware mode transition (`damp` / `stand` / `move`) |
VelocityCommand [#velocitycommand]
| Field | Description |
| ------ | ----------------------------------- |
| `vx` | Forward / backward velocity (m/s) |
| `vy` | Left / right lateral velocity (m/s) |
| `vyaw` | Rotational velocity (rad/s) |
EdgeEvent [#edgeevent]
Delivered over the reliable `system` channel. Carries boot progress, error reports, alert triggers, and mode-transition notifications from the robot. Use it to react to state changes rather than polling telemetry.
# Safety & Handling
Asimov 1 weighs 35 kg and has 25 actuators. Read this page before you start the firmware for the first time, and [Stopping the Robot](/asimov/1/operate/safety/stopping) before your first drive.
The **E-Stop** and every other stop in Asimov Manager are carried out by the robot's own computers. None of them replaces cutting power. Whenever the firmware is running, know how to [cut power](/asimov/1/operate/safety/stopping#cutting-power).
The Robot's States [#the-robots-states]
| State | What the actuators do | Holds itself up? |
| ------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| **Unpowered** | The firmware is stopped. Actuators are offline and every joint moves freely by hand | No |
| **Damp** | The firmware is running. Actuators are powered but limp | No |
| **Stand** | Actuators hold a standing pose | Keep it supported — see [Stand Up and Walk](/asimov/1/operate/drive/stand-up) |
| **Walk** | The locomotion policy balances the robot, and moves it only when you drive | Yes, on a flat floor |
{/* TODO(firmware): SAFETY-CRITICAL — the Cockpit describes Stand as "the robot rises and balances on both feet", but /sdk/safety says STAND stiffens a pose with no balance loop and tips a free-standing robot. Until firmware confirms, these docs treat Stand as needing support. Reconcile the SDK safety page and overview/system-tour/software.mdx either way. */}
Pose, inspect and carry the robot only while it is **Unpowered**. A damped robot is just as limp, but its actuators are live: a posture change, a fault or a stray command can move a joint without warning.
Supporting the Robot [#supporting-the-robot]
Until the robot is walking, support it in one of these ways:
* **Hanging from its gantry hook.** Keeps the legs free for calibration and standing up.
* **Seated on a bench.** Stable enough that the robot can go limp without tipping.
* **Held by a person** at the torso, never the joints, who expects it to go limp.
Support it again before you damp it or turn it off.
An **E-Stop** or a fault makes the robot go limp at once. Keep clear space around a free-standing robot.
{/* TODO(hardware): confirm the official support points, gantry hook rating and recommended bench posture. */}
Lifting and Carrying [#lifting-and-carrying]
{/* TODO(hardware): lifting procedure — mass distribution, grab points, one- vs two-person carry, and securing limbs for transport. */}
Until detailed lifting guidance is published:
. Turn the robot off and [cut power](/asimov/1/operate/safety/stopping#cutting-power).
. Carry it with two people, holding the torso. The limbs swing freely and shift the robot's weight as you move.
Before Every Session [#before-every-session]
* **Clear the area.** Keep a space several times the robot's footprint free of people and obstacles.
* **Use a flat floor.** Never stand or walk the robot on a bench, on a slope or near an edge.
* **Keep it in sight.** The **Cockpit** shows no video in this release.
* **One person drives.** Only one Cockpit session or gamepad controls the robot at a time. Make sure everyone nearby knows who has it.
* **Support it through transitions.** From firmware start until it is walking, and again before it damps or turns off.
* **Know your stops.** Read [Stopping the Robot](/asimov/1/operate/safety/stopping), including how to cut power.
{/* TODO(hardware): give the clear-area size in meters. */}
**Next:** [Set Up](/asimov/1/operate/setup) commissions a newly assembled robot.
# Stopping the Robot
Asimov 1 has five stops, and they leave the robot in very different states. Pick the one whose result you can accept.
| Stop | Where | What happens |
| -------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------- |
| **Pause** | Cockpit: `Space` or **PAUSE** | A walking robot stops moving and keeps balancing in place |
| **E-Stop** | Cockpit, page headers, phone top bar, gamepad | Every actuator damps at once. A free-standing robot **falls**. The firmware keeps running |
| **Damp & shut down** | Cockpit posture controls | The same release as an E-Stop, done deliberately on a supported robot |
| **Turn Off Robot** | Overview | The firmware stops and the actuators go offline. The console stays up |
| **Cut power** | The battery unit | Removes all power. The only stop that doesn't depend on software |
Pause [#pause]
Pause is the everyday stop. It sets the velocity to zero and the robot keeps balancing where it is. The Cockpit shows **STOPPED** and "Velocity zeroed — holding position"; select **Resume Driving** to carry on.
Letting go of the keys, a touch pad or the stick also brings the robot to a halt: input is held, and release means zero velocity.
Use Pause only while the robot is walking. It always commands Walk at zero velocity, so pressing it in Stand or Damp asks the robot to walk.
{/* TODO(manager): Pause sends MOVE(0) from any posture (drive.py:429-444), bypassing the posture ladder. Fix in the Cockpit, then drop the paragraph above. */}
E-Stop [#e-stop]
The E-Stop damps every actuator immediately, with no confirmation. Use it when a limp robot on the floor is better than whatever is happening now.
| Where | How |
| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Cockpit, on a desktop | **E-STOP**, or press `X` |
| Page header on a desktop: Overview, Firmware, Wi-Fi, Controller, Account and Troubleshoot | **E-Stop** — one click |
| Top bar on a phone, on every page | **E-STOP** — press and hold for about half a second. A tap only flashes **HOLD TO E-STOP** |
| A gamepad | `Options` / `☰` — see [Gamepad](/asimov/1/operate/drive/gamepad) |
On a phone, the Cockpit has no **E-STOP** button of its own — use the one in the top bar. **The header and top-bar E-Stops need no drive session:** anyone signed in can stop the robot.
"E-stop sent, actuators damped. Re-arm from the cockpit" confirms the send, not that the robot obeyed — watch the robot. And the **E-Stop** is disabled when **Asimov Edge** isn't running, because there is no path to the robot; use **Turn Off Robot** on Overview instead, or cut power.
{/* TODO(manager): SAFETY-CRITICAL — the phone top-bar E-Stop sends a one-off burst of DAMP packets, which an active Cockpit session's command stream can override (cockpit.js:861-863). Verify that the phone E-Stop stops a robot mid-drive before publishing. */}
Damp & Shut Down [#damp--shut-down]
Damping releases the actuators on purpose, for when the robot is supported and you are done. In the Cockpit, select **Damp**, tick **The robot is physically supported**, then hold **Hold 2s to damp**. [Bring It Back Down](/asimov/1/operate/drive/stand-up#bring-it-back-down) covers the full sequence from walking.
Turn Off Robot [#turn-off-robot]
**Turn Off Robot** on Overview stops the firmware. All 25 actuators go offline and driving stops; Asimov Edge and the console stay up. Support the robot first — see [Power On and Off](/asimov/1/operate/power#turn-the-robot-off).
Cutting Power [#cutting-power]
Press the button on the battery unit to power the robot down. Before working on the electronics, also disconnect the battery from the XT90 connector on the Robot Processing Unit (RPU), as described in [Electronics Disassembly](/asimov/1/build/assembly-preparations/electronics-disassembly).
{/* TODO(hardware): confirm the battery button cuts actuator power immediately and is the intended emergency cut-off, and add a photo of its location. State whether a separate hardware emergency stop is supplied or recommended — /sdk/safety asks for one. */}
Recovering After an E-Stop or a Fall [#recovering-after-an-e-stop-or-a-fall]
After an E-Stop or a fault, the robot is limp but its firmware is still running. Recover in this order:
. **Wait until the robot is still.** Don't try to catch a falling robot.
. **Turn it off.** Select **Turn Off Robot** on Overview, so the actuators are unpowered before you touch the robot.
. **Find out why.** Look for **fault N** on the Cockpit's status line and for **Error flags** in its Telemetry tab, then read the **Firmware** log on the [Troubleshoot page](/asimov/1/operate/troubleshooting#reading-the-logs). Note the time — the logs roll over.
. **Inspect and support it.** Check for damage, then lift the robot back onto its gantry hook or a bench — see [Lifting and Carrying](/asimov/1/operate/safety#lifting-and-carrying). After a hard fall, run **Check Liveness** on the Actuators page before going further.
. **Start it again.** Select **Start Robot** on Overview, then stand it up from its support: [Stand Up and Walk](/asimov/1/operate/drive/stand-up).
Restarting the firmware also clears a fault latched by a fall. Until the firmware restarts, the robot refuses to stand.
If you pressed the E-Stop while the robot was still supported and nothing fell, you can skip straight to re-arming it: take control in the Cockpit and select **Stand**.
# Assign Actuator IDs
Each actuator needs its joint ID before it goes into the robot. You program the number on its sticker, one actuator at a time. Allow about an hour and a half.
Programming writes to whatever is on the setup connector. With more than one actuator connected, it can permanently reprogram the wrong one. Never program with the robot's harness connected.
Before You Start [#before-you-start]
* You are signed in to Asimov Manager, and both RPU checks are green — see [Check the RPU Link](/asimov/1/build/assembly-preparations/software-setup/connect-and-sign-in#check-the-rpu-link).
* All 25 actuators are within reach, each with its numbered sticker.
* You have an XT30 (2+2) cable to connect each actuator to the **setup connector**, the left-leg actuator port on the RPU.
{/* TODO(hardware): name the setup connector as it is labelled on the board, add a photo, name the cable, and say whether to power down between actuators — the console says "unplug it and connect the next actuator" without mentioning power. */}
Enable Programming [#enable-programming]
. Open **Actuators** in the sidebar. It opens on the **Actuator ID** tab.
. Read the warning, then select **I Understand, Enable Programming**.
Programming stays enabled until you leave or reload the page.
If the tools are locked, a pill at the top of the page names the reason:
| Pill | Fix |
| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| **Stop the firmware to change actuators** | Select **Open Firmware ›** and stop the firmware |
| **RPU unreachable** or **RPU not controllable** | See [Check the RPU Link](/asimov/1/build/assembly-preparations/software-setup/connect-and-sign-in#check-the-rpu-link) |
| **Robot state unknown** | Select **Retry** |
Program Each Actuator [#program-each-actuator]
Repeat for all 25 actuators:
**Connect one actuator** to the setup connector.
*Optional:* select **Detect Actuator** to confirm it's connected. **Current ID** shows the ID it holds now.
Type its sticker number, from 1 to 25, in **New ID**.
Select **Program & Verify**. Check the ID and joint in the confirmation against the sticker, then select **Program Actuator**.
Wait for "Actuator is now N · *joint*". Unplug the actuator and set it aside, in sticker order.
* The map marks the lowest unprogrammed ID as **next up**, but any order works.
* **Don't ask again this session** skips the confirmation. Leave it unticked until you have the rhythm.
* Reloading the page clears the map, not the IDs. To recheck an actuator, connect it on its own and select **Detect Actuator**.
If Something Goes Wrong [#if-something-goes-wrong]
| Message | Fix |
| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| "No actuator detected, check power and wiring" | Check that one actuator is connected, powered and firmly cabled. Wait a few seconds, then retry |
| "Couldn't set the ID, check that exactly one actuator is connected and powered" | Disconnect every other actuator, then retry |
| "Enter a sticker ID from 1 to 25" | Enter a number from 1 to 25 in **New ID** |
| The tools are greyed out | Read the pill at the top of the page — see [Enable Programming](#enable-programming) |
| A sticker is missing, unreadable or duplicated | Stop, and sort out the labels before assembly |
Next [#next]
When all 25 are programmed, continue to [Electronics Disassembly](/asimov/1/build/assembly-preparations/electronics-disassembly). During assembly, fit each actuator into the joint that matches its sticker. Afterwards, [Calibrate Actuators](/asimov/1/operate/setup/calibrate-actuators) checks all 25 and records their zero positions.
# Battery Setup
Parts needed [#parts-needed]
| Part | Count |
| --------------------------------------------------------------------- | --------: |
| battery enclosure | 1 |
| enclosure cover | 1 |
| battery with attached cable and connector | 1 |
| charge port | 1 |
| battery switch | 1 |
| M3x12 Flat bolt | 2 |
| M4x8 Socket bolt | 4 |
| Threadlocker | As needed |
Instructions [#instructions]
Place part such that the hollow portion is facing upward toward the ceiling.
Take the battery (part ) and slowly lower it into the hollow portion of part , the cable should be on the opposite side of the cable cut out on part .
There are three groups of wires and cables coming out of the battery:
1. Switch wires (highlighted in purple): the thin black and red wires without a connector at the end.
2. Battery power cable (highlighted in red): the cable group with a plug connector at the end.
3. Charger port cable (highlighted in yellow): the cable with a charger port connector at the end.
To see how to route the cable group properly, please refer to the video below.
Put the charge port (part ) into the protruding mount at the back of part . Make sure that the battery power cable (highlighted in red) is in-between the two parts. Open the cap of the charger port as shown in the picture below.
Take **2 of** M3x12 Flat bolts and tighten them into each of the two holes to secure part to part
Take part and align it with part such that all the holes on part align with the threaded insert on part .
Make sure that the switch wires (highlighted in purple) are routed through the hole on part . Be careful not to pinch the wires.
Take **4 of** M4x8 Socket bolts and tighten them into each of the four holes to secure part and part together.
After the parts are aligned:
* remove **4 of** M4x8 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Take the battery switch (part ) and install it through the round hole on part . Follow the instruction in the video below to see how to do it the right way.
After all is done, you can remove the plastic charger port cover and charge the battery using the provided charger.
For your own safety, please never charge battery overnight or un-attended.
# Electronics Disassembly
With every actuator ID assigned, separate the Head Compute Unit and the RPU again so the RPU can be installed in the torso.
Turn the system off before disconnecting anything. Press the button on the battery unit to power down, then disconnect the battery from the XT90-M(2+2) connector on the RPU. Never unplug data or power cables while the system is live.
Before You Start [#before-you-start]
* All 25 actuator IDs are assigned — see [Assign Actuator IDs](/asimov/1/build/assembly-preparations/assign-actuator-ids).
Disconnect the Head Compute Unit [#disconnect-the-head-compute-unit]
Unplug the 1000 mm Ethernet cable (highlighted in blue) between the RPU and the Raspberry Pi 5 .
Disconnect the 1000 mm XT30-Male to XT30-Female power cable (highlighted in red) that runs between the RPU and the Head Board .
The Head Compute Unit can now be set aside for head assembly later on.
Disconnect the Battery from the RPU [#disconnect-the-battery-from-the-rpu]
Remove the XT30 battery power cable (highlighted in red) from the RPU to disconnect the power fully.
The RPU is now fully disconnected from the Head Compute Unit and the battery, ready for installation later on into the torso.
# Part Organisation
When your kit arrives, check that all the parts are there before you start building.
Work through each module checklist below to confirm you have everything you need to assemble your Asimov.
As you go, inspect each part for any damage from shipping. If anything is missing or damaged, contact us as soon as possible, within the warranty period, and we'll send you a replacement.
Sort parts by module [#sort-parts-by-module]
Every manufactured part number starts with its module number. Sort the parts into one group per module, from 100 to 700, as shown below:
| Number | Module |
| ------ | --------- |
| 100 | Torso |
| 200 | Pelvis |
| 300 | Right arm |
| 400 | Left arm |
| 500 | Right leg |
| 600 | Left leg |
| 700 | Head |
Parts of the same type and design for different modules are sometimes packed in the same bag. For example, `ASV1_600_37C` (left foot) and `ASV1_500_37C` (right foot) come together. Take only the part for the module you are counting, and set the other aside with the module it belongs to, to count when you get there.
To protect them in transit and save packing space, some parts, such as the head and body covers, may already be bolted onto other parts. Unscrew them so you can count each part separately, and keep the bolts with that module's hardware: they are counted in the checklist and used again during assembly.
Cable naming [#cable-naming]
Cable names list the length first, followed by the connector type and purpose where needed, for example 1000 mm XT30-Male to XT30-Female power cable or 200 mm double-ended XT30 (2+2) cable .
Single-ended cables have a connector at one end; double-ended cables have connectors at both ends.
Actuator cables use XT30 (2+2) connectors, distinct from XT30 power-only connectors.
Actuator Type [#actuator-type]
Asimov uses 5 types of actuator. Use the previews below to tell them apart; drag a model to rotate it. We recommend you to sort out actuator by type first as shown below, so it is easier to find them later on.
While you sort out the actuator below, stick a piece of masking tape on each actuator and write its ID on it. This makes it much easier to match each actuator to its ID when you [assign actuator IDs](/asimov/1/build/assembly-preparations/assign-actuator-ids).
There should be a total of 25 actuators. When you sort them by type, remember to use a masking tape and write the ID on the actuator according to the ID list we given below in the 3D visualization. Each actuator should only carry one ID and vice versa, don't assign more than one actuator with the same ID.
From left to right: **EC-A5013-H17-100**, **EC-A3814-H14-107**, **EC-A4315-P2-36**, **EC-A4310-P2-36** and **EC-A6416-P2-25**. The 3D previews below follow the same order.
# Finish Assembly
Confirm that:
* both leg modules are attached to the pelvis
* both arm modules are attached to the torso
* the torso is secured to the pelvis
* the head and RPU are installed
* the front and rear torso covers are secured
* no cable is trapped or visibly damaged
* the robot is mechanically supported
* the battery is connected to the RPU, and the robot is still powered off at the battery unit
After these checks pass, continue to [Set Up](/asimov/1/operate/setup) in the Operate stage to calibrate the actuators and start the robot.
# Assembly Conventions
Bolt Names and Counts [#bolt-names-and-counts]
The manual uses compact bolt names such as M3x8 Socket bolt and M4x12 Flat bolt .
* **M3** is the metric thread size: a nominal thread diameter of 3 mm.
* **x8** means an 8 mm bolt length. The starting point for this measurement depends on the head type, as explained below.
* **Socket** has a raised cylindrical head. **Flat** has a countersunk head with a tapered underside that fits into a matching recess.
For example, "Take **4 of** M3x8 Socket bolts " means select four bolts of that size and type. Use "of" for every count, including one: "Take **1 of** M3x20 Socket bolt ." Parts tables use a singular bolt name and put the quantity in the **Count** column.
Orange, dotted-underlined bolt names open a 3D preview on click or tap. A bolt name without an underline has no verified preview yet. Use the preview to recognize the head shape, not to judge the bolt's length on screen.
Measuring Bolt Length [#measuring-bolt-length]
Use a ruler or caliper and measure in millimetres. Start at the ruler's zero mark, not the edge of the ruler.
| Type | Measure from | Measure to | Include the head? |
| ------ | -------------------------------------------------------- | ------------------- | ----------------- |
| Socket | The flat underside of the head, where it meets the shaft | The end of the bolt | **No** |
| Flat | The flat top surface of the countersunk head | The end of the bolt | **Yes** |
Socket Bolts [#socket-bolts]
Measure from **under the head to the end of the bolt**. Include any unthreaded shaft, but exclude the head itself; do not measure only the threaded section. A M3x8 Socket bolt measures 8 mm under the head, so its overall length is longer than 8 mm.
Flat Bolts [#flat-bolts]
Measure the **entire bolt, including the head**, from its flat top surface to the end. A M4x12 Flat bolt measures 12 mm overall.
Socket and Flat bolts of the same thread size and listed length are not interchangeable. Check the head type and measure the length before installation. Do not force a bolt that does not fit, or substitute a different length to make it fit.
Threadlocker [#threadlocker]
Cut the nozzle tip, squeeze a small pool of threadlocker into the bottle cap, then wipe the bolt threads through it rather than dripping straight from the bottle. That is the easiest way to keep the amount under control. The video below walks through the whole process.
Threadlocker is very hard to undo. If you apply it to a bolt that turns out to be in the wrong hole, the wrong orientation, or the wrong part, you may not be able to back that bolt out again.
You can assemble the whole robot without threadlocker. Leaving it off does not stop the robot going together — it just makes the joints more likely to rattle loose over time. So if you are still checking fit, alignment, or orientation, assemble dry first, confirm everything is right, then remove the bolts in a star formation, apply threadlocker, and retighten.
From M4 upwards, one drop is the right amount. M3 screws need slightly less than that, so do not let a full drop land on one.
Excess threadlocker spreads past the threads and makes the screw much harder to back out later.
If you have already applied threadlocker and need to remove a bolt, heat the bolt head with a soldering iron or similar for a few seconds. The heat softens the threadlocker and the bolt will turn out far more easily than forcing it cold.
Keep the heat on the bolt head only, and keep it away from printed plastic parts, cables, and actuators.
Over-tightening [#over-tightening]
The M3 hex screws are small, and an electric screwdriver rounds out the head. Electric screwdrivers are prohibited on M3 and smaller — turn those by hand.
More generally, avoid an electric screwdriver altogether unless you know the tool well and can control its torque. A hand tool is slower, but it is far harder to strip a head or a thread with one. If you are unsure, use hand tools. See [Tools](/asimov/1/overview/tools) for the recommended set.
Solder [#solder]
# Cables
Cables ship together in one labelled pack, so they are counted here in a single checklist instead of per module. Each row shows the total for the whole robot and what each cable connects.
# Head Parts
This checklist covers everything you need to assemble the head: manufactured parts, actuators, and fasteners. Cables are counted together in the [Cables](/asimov/1/build/assembly-preparations/part-organisation/cables) checklist.
# Left Arm Parts
This checklist covers everything you need to assemble the left arm: manufactured parts, actuators, and fasteners. Cables are counted together in the [Cables](/asimov/1/build/assembly-preparations/part-organisation/cables) checklist.
# Left Leg Parts
This checklist covers everything you need to assemble the left leg: manufactured parts, actuators, and fasteners. Cables are counted together in the [Cables](/asimov/1/build/assembly-preparations/part-organisation/cables) checklist.
# Pelvis Parts
This checklist covers everything you need to assemble the pelvis: manufactured parts, actuators, and fasteners. Cables are counted together in the [Cables](/asimov/1/build/assembly-preparations/part-organisation/cables) checklist.
# Right Arm Parts
This checklist covers everything you need to assemble the right arm: manufactured parts, actuators, and fasteners. Cables are counted together in the [Cables](/asimov/1/build/assembly-preparations/part-organisation/cables) checklist.
# Right Leg Parts
This checklist covers everything you need to assemble the right leg: manufactured parts, actuators, and fasteners. Cables are counted together in the [Cables](/asimov/1/build/assembly-preparations/part-organisation/cables) checklist.
# Torso Parts
This checklist covers everything you need to assemble the torso: manufactured parts, electronics, actuators, and fasteners. Cables are counted together in the [Cables](/asimov/1/build/assembly-preparations/part-organisation/cables) checklist.
# Connect and Sign In
Asimov Manager is the console the robot serves from its Raspberry Pi 5 . No terminal is needed to connect and sign in. Batch 1 customers next [enable the battery BMS switch](/asimov/1/build/assembly-preparations/software-setup/enable-bms-switch), which requires terminal access. All other batches can proceed to [Assign Actuator IDs](/asimov/1/build/assembly-preparations/assign-actuator-ids) in Asimov Manager.
Connect and Power On [#connect-and-power-on]
. Connect the 1000 mm Ethernet cable directly between the Pi 5 in the Head Compute Unit and the RPU. This link is private to the two computers; it doesn't go to your router.
. Power on both assemblies.
. Wait about two minutes for them to boot.
Sign In [#sign-in]
. Put your laptop on the Wi-Fi network you entered when [flashing the Pi 5](/asimov/1/build/assembly-preparations/software-setup/flash-raspberry-pi).
. Open `http://.local`, where `` is the hostname you set in Raspberry Pi Imager.
. Select **Sign in** — the default login is pre-filled. You can [set your own password](#set-a-password) later.
If the Robot Didn't Join Your Wi-Fi [#if-the-robot-didnt-join-your-wi-fi]
The robot starts its own setup hotspot instead.
. Join the network named `asimov-XXXX`, where `XXXX` is the last four characters of the Pi 5 's Wi-Fi MAC address (it tells robots apart if you have several). The password is the 16-digit code from the Pi 5 's retail box — see [Firmware & Software Flashing](/asimov/1/build/assembly-preparations/software-setup).
. Open `http://.local`, or `http://10.42.0.1` if that doesn't resolve.
. Select **Sign in** — the default login is pre-filled.
The hotspot has no internet, so phones often drop it. If you use a phone, turn off mobile data.
You can assign actuator IDs on the hotspot. To move the robot onto your Wi-Fi later, see [Join Your Wi-Fi](/asimov/1/operate/setup/connect#join-your-wi-fi). If the console doesn't load, see [The Console Will Not Load](/asimov/1/operate/troubleshooting#the-console-will-not-load).
Check the RPU Link [#check-the-rpu-link]
Asimov Manager reports the status of the RPU from the controller running on its Radxa CM5 .
Open **Firmware** in the sidebar. Both checks on the RPU card must be green before you continue.
| If this is red | Do this |
| -------------- | --------------------------------------------------------------------------------------------------------- |
| **Network** | Check the 1000 mm Ethernet cable at both ends, and that both assemblies have power |
| **Control** | The RPU is still booting. Wait a minute, then reload the page |
If a check stays red, see [The RPU Is Unreachable](/asimov/1/operate/troubleshooting#the-rpu-is-unreachable).
The Firmware page asks **Is the robot fully assembled?**, and **Start Firmware** is greyed out. Both are expected until the robot is built.
Set a Password [#set-a-password]
Anyone on the robot's network can sign in with the default login. [Set a password](/asimov/1/operate/setup/connect#set-a-password) now.
# Enable the Battery BMS Switch
This step is only for early customers who received a **Batch 1** robot, we will let you know through email if you are a batch 1 customer. If you still are not sure, contact us first through our support channel. **All other batches can skip this step** and continue to [Assign Actuator IDs](/asimov/1/build/assembly-preparations/assign-actuator-ids).
Before You Begin [#before-you-begin]
* Complete [Connect and Sign In](/asimov/1/build/assembly-preparations/software-setup/connect-and-sign-in). The Head Compute Unit and RPU must be connected by the 1000 mm Ethernet cable and powered on.
* Keep your laptop on the same Wi-Fi network as the robot, or on the robot's setup hotspot.
* Have the **hostname**, **username** and **password** you set in [Raspberry Pi Imager](/asimov/1/build/assembly-preparations/software-setup/flash-raspberry-pi) ready, with SSH enabled. Without them you cannot log in to the Pi 5 ; contact us through our support channel.
Open a Terminal [#open-a-terminal]
A **terminal** (also called a **TTY session**) is a window where you type commands and press **Enter** to run them. This step uses **two terminal windows** at the same time: **Terminal 1** watches the battery's messages, and **Terminal 2** sends commands.
Open the **Start** menu, type `Terminal` (or `PowerShell` on older Windows 10), and open it. Windows 10 and 11 already include the `ssh` command.
To open a second terminal, press **Ctrl+Shift+T** for a new tab, or open **Terminal** again from the Start menu. Paste with **Ctrl+V** or a right-click.
Press **Cmd+Space**, type `Terminal`, and press **Enter**.
To open a second terminal, press **Cmd+T** for a new tab or **Cmd+N** for a new window. Paste with **Cmd+V**.
Press **Ctrl+Alt+T**, or open **Terminal** from your applications menu.
To open a second terminal, press **Ctrl+Shift+T** for a new tab or **Ctrl+Shift+N** for a new window. Paste with **Ctrl+Shift+V**.
Placing the two windows side by side makes it easier to watch Terminal 1 while you type in Terminal 2.
Connect to the RPU [#connect-to-the-rpu]
Your laptop cannot reach the RPU directly. You first log in to the Raspberry Pi 5 over **SSH** (a secure remote login), then log in to the RPU from the Pi 5 . Do this in **each** terminal.
Log In to the Pi 5 [#log-in-to-the-pi-5]
Replace `` and `` with the username and hostname you set in Raspberry Pi Imager; for example, `ssh alex@asimov1.local`:
```bash
ssh @.local
```
* The first time you connect, you are asked `Are you sure you want to continue connecting (yes/no/[fingerprint])?`. Type `yes` and press **Enter**.
* Enter your Pi 5 password when asked. **Nothing appears on screen while you type the password**; this is normal. Press **Enter** when done.
* If `.local` is not found and your laptop is on the robot's setup hotspot, use `ssh @10.42.0.1` instead.
When you are logged in, the line where you type starts with `@`.
Log In to the RPU [#log-in-to-the-rpu]
From the Pi 5 , run:
```bash
ssh asimov@10.0.0.2
```
Type `yes` if asked to continue connecting, then enter the RPU password, `asimov`. When you are logged in, the line where you type starts with `asimov@`.
When a command below starts with `sudo`, you may be asked for a password again. Enter the RPU password, `asimov`.
Steps [#steps]
Open Terminal 1 [#open-terminal-1]
Open a TTY session on the RPU (**Terminal 1**), as described in [Connect to the RPU](#connect-to-the-rpu). This session will be used for observing data on the CAN bus.
Start Watching the CAN Bus [#start-watching-the-can-bus]
In Terminal 1, run:
```bash
sudo candump can_bms
```
This keeps running and prints each message on the CAN bus, so you can no longer type commands in this terminal. Leave it open.
Open Terminal 2 [#open-terminal-2]
Open another TTY session on the RPU (**Terminal 2**), the same way. This session will be used for sending commands to the CAN bus.
Read the BMS Register [#read-the-bms-register]
In Terminal 2, run the following command, then look in Terminal 1 and note down the data received from the BMS (the eight bytes after `[8]`):
```bash
sudo cansend can_bms 137#5A
```
Increment Byte 3 [#increment-byte-3]
The register bytes are **hexadecimal** (base 16: digits `0`–`9`, then `A`–`F`). Increment byte 3 (zero-indexed) of the BMS register by 1 in hexadecimal, leaving the other seven bytes unchanged. For example, `00 02 00 06 00 07 00 0D` becomes `00 02 00 07 00 07 00 0D`:
Count in hexadecimal, not decimal: `09` becomes `0A` (not `10`), and `0F` becomes `10`.
`FF` is the largest value a byte can hold, so it cannot be incremented. Do not continue, and do not enter factory mode. Contact us through our support channel.
Save this incremented value; you will need it to update the BMS register after entering factory mode.
Enter Factory Mode [#enter-factory-mode]
In Terminal 2, run the following command to enter factory mode:
```bash
sudo cansend can_bms 12E#5678
```
Write the Updated Register [#write-the-updated-register]
In Terminal 2, run the following command with your incremented register bytes, written without spaces:
If this command executes successfully, the RPU will turn off. Both terminals then stop responding or show a message such as `Connection to 10.0.0.2 closed`; this is expected.
```bash
sudo cansend can_bms 137#
```
For example:
```bash
sudo cansend can_bms 137#000200070007000D
```
Turn the Battery Back On [#turn-the-battery-back-on]
Turn the battery back on using the switch, and wait for the RPU to boot. This takes about two minutes.
Reconnect Both Terminals [#reconnect-both-terminals]
Relaunch the previous TTY sessions for reading and writing to the CAN bus. In both terminals, repeat [Connect to the RPU](#connect-to-the-rpu). If a terminal is still logged in to the Pi 5 , you only need to repeat the RPU login. Then run `sudo candump can_bms` in Terminal 1 again.
Exit Factory Mode [#exit-factory-mode]
In Terminal 2, run the following command to exit factory mode:
```bash
sudo cansend can_bms 12F#2828
```
Close the Terminals [#close-the-terminals]
Press **Ctrl+C** in Terminal 1 to stop `candump`. Type `exit` and press **Enter** to log out; type it twice to leave both the RPU and the Pi 5 , then close the terminals.
Example candump Output [#example-candump-output]
**Read data**
```text
can_bms 137 [1] 5A
can_bms 137 [8] 00 02 00 06 00 07 00 0D
```
**Enter factory mode**
```text
can_bms 12E [2] 56 78
can_bms 12E [1] 5A
```
**Increment byte 3 by 1** (zero-indexed; note `06` becoming `07`)
```text
can_bms 137 [8] 00 02 00 07 00 07 00 0D
can_bms 137 [1] 5A
```
**Exit factory mode**
```text
can_bms 12F [2] 28 28
can_bms 12F [1] 5A
```
# Flash the Raspberry Pi 5
3.1 Install Raspberry Pi Imager [#31-install-raspberry-pi-imager]
Download Raspberry Pi Imager from the official Raspberry Pi site:
NOTE: make sure use version \<2.0 (as per link below):
[Releases · raspberrypi/rpi-imager](https://github.com/raspberrypi/rpi-imager/releases?page=2#release-v1.9.6)
additional info (optional):
[Issue · raspberrypi/rpi-imager](https://github.com/raspberrypi/rpi-imager/issues/1667)
3.2 Write the image — and make the robot yours [#32-write-the-image--and-make-the-robot-yours]
. Insert the microSD card into your laptop.
. Open Raspberry Pi Imager.
. Under **Choose OS**, scroll to the bottom and pick **Use custom**, then select `asimov-rpi-.img.xz`.
. Under **Choose Storage**, select your microSD card.
. Click **Next**.
When Imager offers to apply OS customisation, choose “Edit settings” and apply them. This is where your robot gets its name, your account, and your WiFi — it boots straight onto your network, already yours. (If you skip this, nothing is lost: the robot falls back to broadcasting its own setup network.)
Recommended settings, field by field:
| Setting | What to enter |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Set hostname | Your robot's name — lowercase letters, digits and hyphens (e.g. `asimov1`). This becomes the Asimov Manager address, `http://.local`. Give each robot a unique name if you own several. |
| Set username and password | Tick it. Any username except the reserved `asimov`, `asimov-api` and `asimov-pair`; pick a strong password — it is your login and sudo password. The image ships with no default account: skip this and there is no way to log into the OS (Asimov Manager still works). A reserved username is refused at first boot — the robot then behaves like an uncustomised flash. |
| Configure wireless LAN | Tick it. Enter the WiFi the robot will live on — SSID exactly as broadcast (case-sensitive) and its password. Tick Hidden SSID only if your network is hidden. Set “Wireless LAN country” to the country the robot is in — the dialog often remembers GB from a previous run; a wrong country limits which 5 GHz channels the robot may use. |
| Set locale settings | Tick it and choose your timezone and keyboard layout (log timestamps and console input). |
| Services → Enable SSH | Tick it. “Password authentication” uses the account password from above. Choose “public-key only” instead if you use SSH keys — paste your public key there. |
A 2.4 GHz network is the most reliable choice. The Pi 5 's radio cannot see 5 GHz channels 149–165 at all — if your router is pinned there and the robot never joins, move the router to channels 36–48 or enable a 2.4 GHz SSID.
When writing finishes, insert the microSD card into the Raspberry Pi 5
# Flash the Radxa CM5
The Radxa CM5 has no SD card slot. You flash its onboard storage over USB-C through the RPU, with the CM5 in a special MaskROM mode.
2.1 Install the flashing tool [#21-install-the-flashing-tool]
The tool differs by operating system. Windows uses a graphical tool; macOS and Linux use a command-line one. Open the section for your computer.
On Ubuntu 22.10 or newer, and current Debian:
```bash
sudo apt install rkdeveloptool
```
On older distributions the package may not exist, in which case build it from source:
```bash
sudo apt install -y libudev-dev libusb-1.0-0-dev dh-autoreconf pkg-config build-essential git
git clone https://github.com/rockchip-linux/rkdeveloptool
cd rkdeveloptool && autoreconf -i && ./configure && make -j"$(nproc)"
sudo cp rkdeveloptool /usr/local/sbin/
```
Check it worked:
```bash
rkdeveloptool -V
```
No driver is needed. You will need `sudo` for the flashing commands.
Install via Homebrew — no compiling required:
```bash
brew tap IgorKha/rkdeveloptool
brew install rkdeveloptool
```
If you encounter an untrusted tap error:
```text
Error: Refusing to load formula igorkha/rkdeveloptool/rkdeveloptool from untrusted tap igorkha/rkdeveloptool
```
Fix it via the recovery instructions and run:
```bash
brew trust igorkha/rkdeveloptool
```
Check it worked:
```bash
rkdeveloptool -V
```
If the tap is unavailable, build from source instead:
```bash
# We must use g++ on MacOS when building
brew install automake autoconf libusb pkg-config gcc@15
# Clone and build
git clone https://github.com/rockchip-linux/rkdeveloptool
cd rkdeveloptool && autoreconf -i && CXX=/opt/homebrew/bin/g++-15 ./configure && make
```
Windows uses RKDevTool, a graphical tool, plus a USB driver. Both come from Radxa:
1. Download and extract DriverAssistant v5.14, then run `DriverInstall.exe` as administrator. Without this driver Windows will not recognise the board.
2. Download and extract [RKDevTool v2.96](https://dl.radxa.com/tools/windows/RKDevTool_Release_v2.96-20221121.rar), then launch `RKDevTool.exe`.
3. You will also need [7-Zip](https://www.7-zip.org/) to decompress the `.img.xz` image into a plain `.img` before flashing.
The commands in Step 2.3 below are for macOS and Linux. For the RKDevTool click-through, follow [Radxa's RKDevTool guide](https://docs.radxa.com/en/som/cm/cm5/low-dev/rkdevtool-use) — with the board in MaskROM mode (Step 2.2) it will show “Found One MASKROM Device”, and you write the loader and image from there.
If you have access to a Mac or Linux machine, we recommend using it — that is the path we test against.
2.2 Put the board in MaskROM mode [#22-put-the-board-in-maskrom-mode]
The order matters — the button must already be held down when the board first receives power. Connect power last.
. Plug the USB-C cable provided from your laptop into the USB-C port on the RPU shown above.
. Hold down the MaskROM button on the RPU.
. While still holding it, press the on button on the battery to power the RPU.
. Release the MaskROM button.
. Confirm the laptop sees it:
```bash
rkdeveloptool ld
```
You should see `Mode=Maskrom`. For example:
```text
DevNo=1 Vid=0x2207,Pid=0x350b,LocationID=202 Maskrom
```
If the list is empty, re-seat the cable and repeat — the button must be held as the board is first powered, and the cable must carry data.
2.3 Write the image [#23-write-the-image]
The bootloader file `rk3588_spl_loader.bin` is the `rk3588_spl_loader_v1.15.113.bin` you downloaded in Step 1 — rename it to match, or use its full name in the commands below. Writing takes a few minutes.
When it finishes, unplug the USB-C cable and remove the battery power.
```bash
rkdeveloptool db rk3588_spl_loader.bin
# You should see: Downloading bootloader succeeded.
xz -dk asimov-rpu-.img.xz
rkdeveloptool wl 0 asimov-rpu-.img
# The write process will begin. After a few minutes it will reach:
# Write LBA from file (100%)
rkdeveloptool rd
# You should see: Reset Device OK.
```
On Linux these need `sudo`. The `xz -dk` line decompresses the image and keeps the original; the write itself needs the plain `.img`.
In RKDevTool, with the board showing “Found One MASKROM Device”, load `rk3588_spl_loader.bin` as the loader and the decompressed `asimov-rpu-.img` as the image, then start the write. Step-by-step screens are in [Radxa's RKDevTool guide](https://docs.radxa.com/en/som/cm/cm5/low-dev/rkdevtool-use).
Decompress the `.img.xz` with 7-Zip first — RKDevTool cannot read the compressed file.
# Firmware & Software Flashing
This flow covers downloading the images, flashing both boards, connecting them and signing in to Asimov Manager. Programming the actuators comes next, in [Assign Actuator IDs](/asimov/1/build/assembly-preparations/assign-actuator-ids).
After **Connect and Sign In**, Batch 1 customers must [enable the battery BMS switch](/asimov/1/build/assembly-preparations/software-setup/enable-bms-switch). All other batches can skip that step and continue to actuator ID assignment.
Before you begin [#before-you-begin]
In the box [#in-the-box]
* Raspberry Pi 5 - the robot's main computer in the Head Compute Unit, runs Asimov Manager
* Radxa CM5 - the RPU's processor, runs actuator control
* 25 actuators — each with a numbered sticker (that number is its ID)
* 1000 mm Ethernet cable , power supplies, frame and hardware
IMPORTANT! Keep the Pi 5 's retail box or scan the QR and record down the 16-digit code. The 16-digit code is your robot's fallback AP WiFi password — it is unique to your board.
Note: the QR code can also be found on the back of the Pi 5 just in case you have lost the box.
You will also need [#you-will-also-need]
* A laptop with a USB-C port and WiFi
* A data-capable USB-C cable (charge-only cables will not work)
* A microSD card, 32 GB or larger
You will install two free tools along the way — one for each board. Both are covered in the steps below, with instructions for Windows, macOS, and Linux.
Step 1 · Get the two images [#step-1--get-the-two-images]
Asimov's software is still a work in progress, so we share the board images directly, along with instructions for using them safely. **Existing customers can reach us through their usual support channel.** Everyone else, [fill in this form](https://tally.so/r/J9O5BJ) and we'll be in touch.
| Image | Download | Asset |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------ | --------------------------------------------- |
| Radxa CM5 | Contact us | {`asimov-rpu-${release}.img.xz`} |
| CM5 SPL bootloader | [Radxa](https://dl.radxa.com/rock5/sw/images/loader/rk3588_spl_loader_v1.15.113.bin) | `rk3588_spl_loader_v1.15.113.bin` |
| Raspberry Pi 5 | Contact us | {`asimov-rpi-${release}.img.xz`} |
| Checksums (optional) | Contact us | `SHA256SUMS` |
The SPL bootloader is needed for flashing the CM5 in Step 2. The Pi 5 image is loaded into Raspberry Pi Imager in Step 3. The image filenames retain the `asimov-rpu` and `asimov-rpi` software names.
Check your downloads (optional) [#check-your-downloads-optional]
Put `SHA256SUMS` in the same folder as the downloaded files, then run:
```bash
# Linux
sha256sum -c SHA256SUMS --ignore-missing
# macOS
shasum -a 256 -c SHA256SUMS --ignore-missing
```
Each downloaded file should report `OK`. On Windows, run `Get-FileHash ` in PowerShell and compare the result with the matching line in `SHA256SUMS`.
Use images from the same release round for both boards. The two boards run matched software; mixing an old image with a new one can leave joints uncontrolled. Each release notes which CM5 and Pi 5 image versions belong together.
# Head Compute Unit Setup
The Head Compute Unit consists of the Head Board at the base, the Raspberry Pi 5 , and the Media HAT Board on top. The Media HAT Board provides the buzzer and helps prevent Pi 5 brownouts.
Parts needed [#parts-needed]
| Part | Count |
| ----------------------------------------------------------------- | ----: |
| head carrier | 1 |
| camera module | 1 |
| Head Board | 1 |
| Raspberry Pi 5 | 1 |
| RPI5 RTC Battery | 1 |
| Media HAT Board | 1 |
| head base | 1 |
| M2.5x6 Socket bolt | 8 |
| M2.5x20 hex standoffs | 4 |
| M4x14 Socket bolt | 4 |
| USB-A to JST-ZHR4 cable (highlighted in yellow) | 1 |
| 1000 mm XT30-Male to XT30-Female power cable (highlighted in red) | 1 |
| 1000 mm Ethernet cable (highlighted in blue) | 1 |
| 100 mm USB-C to USB-A cable (highlighted in purple) | 1 |
| 120 mm JST-XH to JST-XH cable (highlighted in green) | 1 |
| 300 mm single-ended JST-XH cable (highlighted in orange) | 1 |
Instructions [#instructions]
Take part , which is the camera module, and fit it into the square holes on part . Make sure that the camera module has its port at the bottom, as shown in the visualization below.
Take **4 of** M2.5x6 Socket bolts and tighten them into the 4 holes to secure the camera module to part .
Take the Head Board and cut all the bottom pins using the flush cutters, as shown in the video below.
After the bottom pins of the Head Board are trimmed flush, put it on the platform of part . Make sure that the 4 mounting holes align with each other.
Take **4 of** M2.5x20 hex standoffs and tighten them into the 4 threaded holes on top of the Head Board to secure it to part .
Take the Pi 5 and align its 4 mounting corners with the 4 M2.5x20 hex standoffs .
Take **4 of** M2.5x6 Socket bolts to secure the Pi 5 to the 4 M2.5x20 hex standoffs .
Remember to take a picture of the QR code on the box of the Pi 5. It contains the password to the Pi 5 Access Point which allow you to ssh into it remotely. It is unique to each Pi so don't lose it until the set up is complete.
Take the Media HAT Board and install it onto the Pi 5 pins according to the visualization below.
Take part and put it on a flat surface with the inside of the part facing toward the ceiling.
Take the provided USB-A to JST-ZHR4 cable (highlighted in yellow) and plug the JST connector end into the camera module . Then, route the cable through part until the USB-A connector is accessible through the back of the head.
Now we will need to make the 1000 mm XT30-Male to XT30-Female power cable (highlighted in red).
Take 1 XT30-Male connector and 1 XT30-Female connector and follow the video instruction below to learn how to make the cable.
Once the cable is made, plug the male connector into the Head Board as shown in the visualization below. Then, route the cable through part as above, but through the big gap at the back of the head instead.
Then, align the 4 mounting holes on part with those on part according to the visualization below.
Take **4 of** M4x14 Socket bolts and tighten them into the 4 threaded holes on part to secure it to part .
Take the USB connector end of the USB-A to JST-ZHR4 cable (highlighted in yellow) and plug it into the Pi 5 USB port as shown in the visualization below.
Take the 1000 mm Ethernet cable (highlighted in blue) and plug one end into the Pi 5 Ethernet port as shown. Then, route the cable through the gap at the back of the head.
Take the 100 mm USB-C to USB-A cable (highlighted in purple) and connect the Head Board USB-C port to the Pi 5 USB port as shown in the visualization below.
Take the 120 mm JST-XH to JST-XH cable (highlighted in green) and connect the Media HAT Board JST port to the Head Board JST port as shown in the visualization below.
Take the RPI5 RTC Battery (a coin battery) and plug it into the RTC battery port on the Pi 5 shown below. It keeps the Pi 5 clock running while the robot is powered off.
Now, we need to make the speaker wire. Take the given JST-XH cable and cut it in half. Then extend the two `LSP+` and `LSP-` pins to a length of 30cm using AWG 26 wire (which is given in the cable bag). To know which one is the `LSP+` and `LSP-` you can actually use the lable on the port on the Head Board spearker port below to figure it out. See the image and video below for more information.
The Head Board can actually support up to 2 speaker, so the `LSP` is for the Left Speaker and the `RSP` is for the Right Speaker. You can remake the JST-XH cable to support more speaker in the future if you want to.
Once the cable is made, plug it into the Head Board JST port as shown below. At the bare cable end, we will connect it to a WAGO, which will be connected to the speaker cable later on.
# Integration Setup
Connect the Head Compute Unit and the Robot Processing Unit (RPU), then connect the battery to the RPU.
Parts needed [#parts-needed]
| Part | Count |
| ---------------------------------------------- | ----: |
| Battery assembly from Battery Setup | 1 |
| RPU from RPU Setup | 1 |
| Head Compute Unit from Head Compute Unit Setup | 1 |
Instructions [#instructions]
Place the battery assembly, the RPU and the Head Compute Unit on a flat surface.
Take the battery power cable (highlighted in red) and plug it into the XT90-M (2+2) power port on the RPU as shown below.
Take the 1000 mm Ethernet cable (highlighted in blue) from the Head Compute Unit and plug it into the Ethernet port on the RPU as shown below.
Take the 1000 mm XT30-Male to XT30-Female power cable (highlighted in red) from the Head Compute Unit and plug it into one of the three distributed power ports on the RPU as shown below.
The initial setup is complete. Now you are ready to flash the Raspberry Pi 5 and the CM5 with the appropriate software in the next step.
You can turn the system on by pressing the button on the switch of the battery module. The lights on the head board and Radxa CM5 should turn on with a solid LED. You can now proceed to the image flashing process.
# RPU Setup
The Robot Processing Unit (RPU) consists of the Radxa Carrier Board , the Radxa CM5 mounted on it, and the Power Distribution Board (PDB) on top. The Carrier Board with the CM5 mounted on it is the Motion Control Board (MCB); adding the PDB completes the RPU.
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------------------------------------------- | --------: |
| fan mount | 1 |
| Fan | 1 |
| Radxa Carrier Board | 1 |
| Radxa CM5 | 1 |
| Power Distribution Board | 1 |
| M3x8 Socket bolt | 2 |
| M3x18 Socket bolt | 4 |
| 4-pin JST-XH cable | 1 |
| WAGO connectors or approved soldering and insulation supplies | As needed |
Instructions [#instructions]
Align the 2 holes on part onto the U-shape area of the PDB .
Take **2 of** M3x8 Socket bolts to tighten into the PDB to secure the part
Take the fan and align the 4 holes on each corner to the holes on part . Make sure that the fan pushes air downwards through the U-shape hole on the PDB as indicated by the arrow on the fan (refer to the image below for a clearer look).
Take **4 of** M3x18 Socket bolts and secure the fan to part .
Grab the Carrier Board with the Ethernet RJ45 port facing away from you.
Take the CM5 out of the package and lightly place it into the corresponding pins of the Carrier Board in the orientation shown below in the images and visualization.
Evenly press down on the left, right and bottom of the Carrier Board till the CM5 module slides into place. Apply pressure evenly to ensure all pins have an even contact with the Carrier Board .
It should make a "click" sound once it sit in place correctly. Watch the video below for more instruction.
Align the PDB with the Carrier Board via the 2.54 mm pins, pressing down gently till the connectors are in full contact. Refer to the visualization below to install the two boards together.
Prepare the power connection for the fan by connecting it to a 4-pin JST-XH cable using Wago as shown in the image below.
Connect the fan wire to the 2 center wires of the cable (You should be able to see in the image what the white cable only has 2 middle wire instead of all 4 wires, the 2 wires on each end has been cut and not used). You could also solder the wire, if you prefer a cleaner solution.
Plug the fan power connector into the port shown in the visualization and image below.
# Step 14: Complete the Left Arm Module
Congratulations! You have finished assembling the Left Arm module and can keep it ready for torso installation.
We encourage you to share your progress with your friends and loved one, and with our community!
If you do, please feel free tagging us, we would love to give you a shoutout! It is a momentous event for both you and us!
# Step 10: Attach the Elbow Housing Plates
Parts needed [#parts-needed]
| Part | Count |
| ----------------------------------------------- | --------: |
| Elbow actuator-and-housing assembly from Step 9 | 1 |
| | 1 |
| | 1 |
| M4x16 Flat bolt | 10 |
| M3x12 Flat bolt | 6 |
| Threadlocker | As needed |
Instructions [#instructions]
Take part and align all the 5 holes on the part to fit the threaded insert on part . It should cover the actuator bottom near the port, not the actuator shaft.
Take **5 of** M4x16 Flat bolts and lightly tighten into 5 holes on part to align with part .
Flip the assembly over such that the actuator shaft is not facing toward the ceiling.
Take part and align all the 5 holes on the part to fit the threaded insert on part .
Take **6 of** M3x12 Flat bolts and lightly tighten into 6 holes on part to align with the threaded holes on the actuator.
Take **5 of** M4x16 Flat bolts and lightly tighten into 5 holes on part to align with part .
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
After the parts are aligned:
* remove **5 of** M4x16 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Flip the assembly over and repeat for the **5 bolts** on part from Instruction 2:
* remove **5 of** M4x16 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
# Step 11: Attach the Elbow Shaft Plate and Actuator Housing
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------ | --------: |
| Assembly from Step 10 | 1 |
| | 1 |
| | 1 |
| M3x16 Flat bolt | 6 |
| M4x10 Flat bolt | 3 |
| Threadlocker | As needed |
Instructions [#instructions]
Take the assembly from Step 10 and lay it flat on a surface such that the actuator is facing upward toward the ceiling.
Take part and align the 6 holes on it with with the actuator shaft.
Take **6 of** M3x16 Flat bolts and lightly tighten into the 6 holes on part to align with the actuator shaft.
Take part and rotate it such that the port cut out on it is facing toward you. Align the 3 holes on part with the 3 holes on part .
Take **3 of** M4x10 Flat bolts and lightly tighten into the 3 holes on part to align with part .
After the parts are aligned:
* remove **6 of** M3x16 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
After the parts are aligned:
* remove **3 of** M4x10 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
# Step 12: Install and Connect the Wrist Actuator
Parts needed [#parts-needed]
| Part | Count |
| --------------------------------------------------------------------- | --------: |
| Assembly from Step 11 | 1 |
| | 1 |
| | 1 |
| | 1 |
| EC-A4310-P2-36 actuator (ID 17) | 1 |
| M4x10 Flat bolt | 3 |
| M3x12 Flat bolt | 16 |
| Threadlocker | As needed |
Instructions [#instructions]
Take assembly from Step 11 and flip it around such that part with the bearing is facing the ceiling. Take part , align the protrusion with the bearing on part and align the 3 holes on it with part .
Take **3 of** M4x10 Flat bolts and lightly tighten into the 3 holes on part to align with part . After the parts are aligned:
* remove **3 of** M4x10 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Take the EC-A4310-P2-36 actuator (ID 17), align the port with the port cut-out on part , then slot it into the hollow part.
Take part and align it 4 holes at 4 corners with the 4 threaded holes on part . Also, align the 6 holes inner holes on the part with the threaded holes on the actuator shell.
Take **10 of** M3x12 Flat bolts and lightly tighten into the 10 holes on part to align with and the actuator.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts from the inner holes one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
After the parts are aligned:
* remove **4 of** M3x12 Flat bolts from the corner holes one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Take part and align the 6 holes on it with the 6 threaded holes on the actuator shaft.
Take **6 of** M3x12 Flat bolts and lightly tighten into the 6 holes on part to align with the actuator.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Connect the free connector of the double-ended XT30 (2+2) cable from the EC-A4310-P2-36 actuator (ID 16) to the port on actuator (ID 17).
# Step 9: Install the Elbow Actuator and Route Its Cables
Parts needed [#parts-needed]
| Part | Count |
| --------------------------------------------------------------------- | ----: |
| EC-A4310-P2-36 actuator (ID 16) | 1 |
| 100 mm single-ended XT30 (2+2) cable | 1 |
| 230 mm double-ended XT30 (2+2) cable | 1 |
| | 1 |
Instructions [#instructions]
Connect the XT30 (2+2) connector of the 100 mm single-ended XT30 (2+2) cable to one port on the EC-A4310-P2-36 actuator (ID 16) and one connector of the 230 mm double-ended XT30 (2+2) cable to the other actuator port. Put the assembly aside.
Take part and lay it flat on the surface. Make sure that the port cut out on the body is facing upward toward the ceiling. And the port cut out on the side is facing toward you.
Align the actuator with the round hole on part . Route the single-ended XT30 (2+2) cable through the hollow body and the double-ended XT30 (2+2) cable through the cable channel until its free connector exits the side cutout. Make sure that the LED on the actuator align with the LED cut out (above the cable port cut out) on part also.
# Step 7: Connect and Join the Shoulder Assemblies
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------ | --------: |
| Shoulder assembly from Step 2 | 1 |
| Upper arm assembly from Step 6 | 1 |
| 100 mm single-ended XT30 (2+2) cable | 1 |
| | 1 |
| | 1 |
| M4x12 Flat bolt | 8 |
| M3x16 Flat bolt | 6 |
| Threadlocker | As needed |
| Approved soldering and insulation supplies | As needed |
Instructions [#instructions]
Connect the XT30 (2+2) connector of the 100 mm single-ended XT30 (2+2) cable to the port on the shoulder roll EC-A4315-P2-36 actuator (ID 14) in the upper arm assembly.
Solder each corresponding conductor from the cable leads routed from the EC-A5013-H17-100 actuator (ID 13) to the cable leads connected to actuator (ID 14), following the released pinout.
Follow the video below for the soldering process to make sure that the connection is made correctly else it can cause signal infidelity in the system.
Lay the upper arm assembly on its side such that the part with the bearing faces upward.
Lay the shoulder assembly on its side such that the holes on part face upward. This part is symmetrical so any side is fine.
Align the protruded portion of part with the bearing, then align the 4 holes arranged in a moon shape with part from the shoulder assembly.
Take **4 of** M4x12 Flat bolts and lightly tighten into the 4 holes on part to align the part with part
After the parts are aligned:
* remove **4 of** M4x12 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Now flip the entire assembly to the opposite side, with the actuator shaft on the upper arm assembly facing upward.
Align the 6 circular holes on part with the 6 threaded holes on the actuator shaft, then align the 4 moon-shaped holes with the 4 threaded holes on part .
Take **6 of** M3x16 Flat bolts and then lightly tighten them in the 6 holes arrange in a circle on Part to the actuator shaft.
After the parts are aligned:
* remove **6 of** M3x16 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take **4 of** M4x12 Flat bolts and lightly tighten into the 4 holes arrange in a moon shape on part to align with part .
After the parts are aligned:
* remove **4 of** M4x12 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
# Step 8: Attach the Shoulder Yaw Joint Plate and Shaft Mount
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------- | --------: |
| Current shoulder assembly from Step 7 | 1 |
| | 1 |
| | 1 |
| M3x12 Flat bolt | 8 |
| M4x20 Socket bolt | 6 |
| Threadlocker | As needed |
Instructions [#instructions]
Now move Upper Arm Assembly upward so that the shaft of the Left Shoulder Yaw EC-A3814-H14-107 actuator (ID 15) is facing the ceiling.
Take part and align it with the holes on the surface of Upper Arm Assembly around the actuator shaft. Part has 1 protrusion on its edge, it is important that this protrusion is facing toward you like in the visualization. Make sure this is positioned correctly, else your arm will be locked wrongly.
Take **8 of** M3x12 Flat bolts and lightly tighten it into the 8 holes on Part to align with the threaded holes on the actuator.
After the parts are aligned:
* remove **8 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take Part and align the 6 holes on the part with the actuator shaft holes. Also route the cable form the hollow shaft of the actuator through the center of part .
Take **6 of** M4x20 Socket bolts and lightly tighten each one into the holes to align part with the actuator.
After the parts are aligned:
* remove **6 of** M4x20 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
# Step 1: Mount the Shoulder Actuator
Parts needed [#parts-needed]
| Part | Count |
| ----------------------------------------------------------------------- | --------: |
| EC-A5013-H17-100 actuator (ID 13) | 1 |
| | 1 |
| M4x8 Flat bolt | 12 |
| Threadlocker | As needed |
Instructions [#instructions]
Put the EC-A5013-H17-100 actuator (ID 13) on a flat surface, the port should be at the bottom, and the actuator shaft should be upward toward the ceiling.
Take Part and put the actuator through the center of the part, and align the holes on the actuator with the holes around the circumference of the Part .
Using **12 of** M4x8 Flat bolts , tighten each of them lightly into the holes, to keep the actuator and Part aligned first.
After the parts are aligned:
* remove **12 of** M4x8 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
# Step 2: Attach the Shoulder Base and Route the Cable
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------------------------------------------------------------------------------------- | --------: |
| Shoulder actuator assembly containing EC-A5013-H17-100 actuator (ID 13) from Step 1 | 1 |
| | 1 |
| M5x25 Socket bolt | 6 |
| 180 mm single-ended XT30 (2+2) cable | 1 |
| Threadlocker | As needed |
Instructions [#instructions]
Take part and align it with the EC-A5013-H17-100 actuator (ID 13), with the round center facing upward, and the four corner leg facing towrad the actuator shaft. Make sure that the 6 holes on part align with the threaded holes on the actuator shaft.
Take **6 of** M5x25 Socket bolts and lightly tighten into the 6 holes that you see on part to align the part with the actuator.
After the parts are aligned:
* remove **6 of** M5x25 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Connect the XT30 (2+2) connector of the 180 mm single-ended XT30 (2+2) cable to the actuator port nearest the center, then route the cable leads through the hollow shaft.
# Step 13: Connect the Shoulder and Elbow Assemblies
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------- | --------: |
| Shoulder and upper arm assembly from Step 8 | 1 |
| Forearm assembly from Step 12 | 1 |
| WAGO connector | 4 |
| M4x12 Socket bolt | 6 |
| M4x30 Socket bolt | 12 |
| Threadlocker | As needed |
Instructions [#instructions]
Position the shoulder and upper arm assembly and the forearm assembly so the color-coded cable leads from the EC-A3814-H14-107 actuator (ID 15) and the EC-A4310-P2-36 actuator (ID 16) are accessible. Insert each corresponding conductor pair into four WAGO connectors according to the released pinout, inspect each connection, and place the joined cable leads inside part .
If you have never worked with a WAGO connector before, follow the video below to strip, insert, and check each wire before continuing.
The shoulder and upper arm assembly and the forearm assembly should be connected to each other as follows. The orientation of the forearm assembly is very important as it will dictate the rotation limit of the arm, so make sure you oriented it exactly as shown below.
Fit the forearm assembly onto the shoulder and upper arm assembly such that the 6 holes on circumference of part is aligned with the 6 holes on circumference of part .
Take **6 of** M4x12 Socket bolts and lightly tighten into the 6 holes on the circumference of part to align with part
After the parts are aligned:
* remove **6 of** M4x12 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Take **12 of** M4x30 Socket bolts and insert it into the 12 holes on part . This will be used later to install the left arm module to the torso module. If the shoulder part is blocking you from insert the bolt, use a bit of force to rotate the actuator around so you can slot the bolt in.
# Step 3: Prepare the Bearing Housing
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------------------------------------- | ----: |
| | 1 |
| M4 square nuts | 3 |
| M4x16 Socket bolt | 3 |
| Flanged ball bearing 4390N159 | 1 |
Instructions [#instructions]
Take part (the part with a side cable channel, not part ) and place it on a flat surface with the inner hollow facing upward.
Place **3 of** M4 square nuts into the 3 slots on the surface of Part .
Using **3 of** M4x16 Socket bolts and lightly tighten it on the circumference of Part where you have previously inserted the square nuts to secure the square nuts in place first. If you over tighten, it will prevent the actuator from fitting in later on.
Place the flanged ball bearing 4390N159 into the bearing hole on Part .
# Step 4: Prepare the Actuator Housing
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------------------------------------- | ----: |
| | 1 |
| M4 square nuts | 7 |
| M4x16 Socket bolt | 3 |
| M4x25 Socket bolt | 4 |
Instructions [#instructions]
Take part , put it on a flat surface with the inner hollow part facing upward
Put **3 of** M4 square nuts into the 3 slots on the surface of Part
Using **3 of** M4x16 Socket bolts and lightly tighten it on the circumference of Part where you have previously inserted the square nuts to secure the square nuts in place first. If you overtighten, the actuator will not fit in later on.
Put **4 of** M4 square nuts into the 4 slots on the round circumference of Part
Using **4 of** M4x25 Socket bolts and lightly tighten it on the circumference of Part where you have previously inserted the 4 square nuts to secure the square nuts in place first.
# Step 5: Connect the Shoulder Cable Connectors and Position the Actuators
Parts needed [#parts-needed]
| Part | Count |
| ----------------------------------------------------------------------- | --------: |
| Actuator housing from Step 4 | 1 |
| EC-A4315-P2-36 actuator (ID 14) | 1 |
| EC-A3814-H14-107 actuator (ID 15) | 1 |
| 100 mm double-ended XT30 (2+2) cable | 1 |
| 180 mm single-ended XT30 (2+2) cable | 1 |
| M4x8 Socket bolt | 3 |
| M3x12 Flat bolt | 6 |
| Threadlocker | As needed |
Instructions [#instructions]
Take part from Step 4 and put it flat on a surface, with the inner hollow part facing upward.
Place the EC-A4315-P2-36 actuator (ID 14) into the round hollow. The ports on the actuator should be near to the top, and the shaft facing the bottom. The two ports of the actuator should be parallel with the length of .
Connect one connector of the 100 mm double-ended XT30 (2+2) cable to the outward-facing port on actuator (ID 14) and the other connector to the outer port on the EC-A3814-H14-107 actuator (ID 15).
Connect the XT30 (2+2) connector of the 180 mm single-ended XT30 (2+2) cable to the center port on actuator (ID 15), then route the cable through the actuator's hollow shaft.
Turn the current assembly upside down.
Align the threaded holes on the shell of actuator (ID 15) with the three hole at the bottom of the long end of Part .
Take **3 of** M4x8 Socket bolts and lightly tighten into the three hole on part to align the part with the actuator.
After the parts are aligned:
* remove **3 of** M4x8 Socket bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take **6 of** M3x12 Flat bolts and lightly tighten into the 6 holes on the surfaces of Part to align the part with the actuator.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Tighten the **3 bolts** we have put in place in Step 4 Instruction 3 around the circumference of the part. Don’t over tighten it, it is just meant to secure the actuator in place.
# Step 6: Close the Shoulder Actuator Housing
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------- | --------: |
| Actuator housing assembly from Step 5 | 1 |
| Bearing housing from Step 3 | 1 |
| M4x8 Socket bolt | 3 |
| Threadlocker | As needed |
Instructions [#instructions]
Put the part (the part with the actuator secured to it) flat on a surface, such that the actuator is facing upward toward the ceiling.
Slowly remove **4 of** M4x25 Socket bolts , one bolt at a time around the round surface edge surrounding the actuator. Do it slowly so you can avoid the square nut from falling out of the slots.
Put the Part and Part together such that the actuator is well encapsulated and all the holes on the surface aligned with each other.
Take each of the **4 bolts** you just removed and then secure it through the aligned hole from Part .
Tighten the **3 bolts** we have put in place in Step 3 Instruction 3 around the circumference of the part . Don’t over tighten it, it is just meant to secure the actuator in place.
Take **3 of** M4x8 Socket bolts and lightly tighten into the three hole on the longe end of part to align the part with the actuator.
After the parts are aligned:
* remove **3 of** M4x8 Socket bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
# Step 13: Join the Ankle Assemblies
Parts needed [#parts-needed]
| Part | Count |
| ---------------------------------------------------------------------------------------- | ----: |
| Leg assembly from Step 10 | 1 |
| Foot assembly from Step 12 | 1 |
| M4 nylon-insert nuts | 2 |
| M4x25 Socket bolt | 2 |
| Ball Joint Rod End | 2 |
| 194 mm Stainless Steel Bar | 1 |
| 124 mm Stainless Steel Bar | 1 |
| M3x6 Socket bolt | 2 |
| M3 steel washer | 2 |
Instructions [#instructions]
Take the leg assembly from Step 10 and put it on the surface such that the back side of the knee is facing upward towards the ceiling and the ankle is facing toward you.
Take **2 of** M4 nylon-insert nuts and slot them into the 2 holes at the ankle part, Make sure that the white nylon part is facing each other toward the center.
Take the foot assembly from Step 12 and slot part to fit into the ankle portion on part . Make sure that the holes are aligned with the locknuts.
Take **1 of** M4x25 Socket bolt and insert it into the hole on part from the heel direction and tighten it all the way to secure the part to Part .
Turn the current assembly 180 degrees so that the knee is now facing upward toward the ceiling.
Take **1 of** M4x25 Socket bolt and insert it into the hole on part from the toe direction and tighten it all the way to secure the part to Part .
Take the 194 mm Stainless Steel Bar and the 124 mm Stainless Steel Bar and thread each one onto its Ball Joint Rod End on the foot assembly, as shown in the visualization below. You have to keep threading until you don't see any of the thread on the Ball Joint Rod Ends exposed anymore.
Please watch the attached video to know how to do this as it has significant impact to the performance of the locomotion policy of the robot.
Take **2 of** Ball Joint Rod Ends and thread each one into the top of one of the 2 bars above. Then, insert them onto part and part . Do the same thing as above to ensure you cannot see any thread left exposed on the Ball Joint Rod End.
Take **2 of** M3x6 Socket bolts and **2 of** M3 steel washers . Place each washer on each bolt separately and tighten it onto part and part to secure the 2 Ball Joint Rod Ends .
# Step 14: Complete the Left Leg Module
Congratulation! You have finished the assembly of the Left Leg Module and also the first component of your Asimov!
We encourage you to share your progress with your friends and loved one, and with our community!
If you do, please feel free tagging us, we would love to give you a shoutout! It is a momentous event for both you and us!
# Step 11: Install the Linkage Shaft
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------------------------------------------------------------------- | --------: |
| | 1 |
| | 1 |
| | 1 |
| 6mm D-Shaft Stainless Steel | 1 |
| 6mm Bore 1-Side, 2-Post Pillow Block | 2 |
| Ball Joint Rod End | 2 |
| Steel Set-Screw Collar | 2 |
| M4x16 Flat bolt | 11 |
| Threadlocker | As needed |
Instructions [#instructions]
Take the 6mm D-Shaft Stainless Steel and put **2 of** Steel Set-Screw Collars onto it. Move it to the middle of the D-Shaft. Slightly tighten each set screw to keep the collar in place.
Take **2 of** Ball Joint Rod Ends and put one each on the left and right of the D-Shaft.
Take **2 of** 6mm Bore 1-Side, 2-Post Pillow Blocks and put one each on the left and right of the D-shaft. Make sure that the bearing facing outward on the Pillow Block is sunken into the hole instead of flat against it. Please view the visualization in detail to install it correctly.
Take part and fit it onto part as shown below.
Then take the D-shaft component we have been building and align it with part and according to the visualization below.
Flip the assembly upside down so part is visible. Take **4 of** M4x16 Flat bolts and lightly tighten them into the holes on part to align with the threaded holes in the two pillow-block legs.
After the parts are aligned:
* remove **4 of** M4x16 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and the 2 Pillow Block.
Take part and align it with the holes on part as shown below
Take **7 of** M4x16 Flat bolts and tighten them into the 7 holes on part as shown below to secure it to part .
After the parts are aligned:
* remove **7 of** M4x16 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Turn the current subassembly around such that part is facing downward toward the floor. Push the 2 Ball Joint Rod Ends flush against the 2 Pillow Blocks, then tighten the set screw on each Steel Set-Screw Collar to secure them in place.
# Step 12: Assemble the Foot Pivot
Parts needed [#parts-needed]
| Part | Count |
| --------------------------------------------------------------------------- | --------: |
| Foot assembly from Step 11 | 1 |
| | 1 |
| | 1 |
| 12mm Bore 1-Side, 2-Post Pillow Block | 4 |
| M4x20 Socket bolt | 4 |
| M4x16 Flat bolt | 4 |
| Threadlocker | As needed |
Instructions [#instructions]
Take part and put it on a flat surface. Oriented it such that the part is standing on the portion with the two little legs.
Take **2 of** 12mm Bore 1-Side, 2-Post Pillow Blocks and fit them into the 2 poles nearest to the top of the part. The legs of the Pillow Block should be pointing upward toward the ceiling. Make sure that the bearing facing outward on the Pillow Block is sunken into the hole instead of flat against it. Please view the visualization for the correct orientation of the bearing.
Take part and put it on top of the legs of the Pillow Block. Make sure the 4 holes on the surface of the circumference of part aligned with the Pillow Block leg.
Take **4 of** M4x20 Socket bolts and lightly tighten them into the 4 holes on the circumference of part to align it to the 2 Pillow Blocks.
After the parts are aligned:
* remove **4 of** M4x20 Socket bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and the 2 Pillow Blocks.
Now flip the current assembly upside down so that the part is facing downward toward the floor. Put the assembly to one side.
Take the assembly with part and put the remaining **2 of** 12mm Bore 1-Side, 2-Post Pillow Blocks onto the 2 poles that have not been used yet on part . Make sure that the legs of the 2 Pillow Block are facing upward toward the ceiling.
Take the foot assembly from Step 11 and try to align the 2 Pillow Block on part with part .
Take **4 of** M4x16 Flat bolts and lightly tighten into the 4 holes of part that are aligned with the 2 Pillow Block to align the parts.
After the parts are aligned:
* remove **4 of** M4x16 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and the 2 Pillow Block.
# Step 10: Install the Linkage Cranks and Cover
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------ | --------: |
| Current assembly from Step 9 | 1 |
| | 1 |
| | 1 |
| | 1 |
| M3x12 Flat bolt | 16 |
| 100 mm double-ended XT30 (2+2) cable | 1 |
| Threadlocker | As needed |
Instructions [#instructions]
Take part and align it with the threaded hole on the actuator nearer to the ankle on part .
Take **6 of** M3x12 Flat bolts and then lightly tighten them into the holes on part to align it with the threaded holes on the actuator.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take part and align it with the threaded hole on the actuator nearer to the knee on part .
Take **6 of** M3x12 Flat bolts and then lightly tighten them into the holes on part to align it with the threaded holes on the actuator.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
After part and is secured to the actuators. We need to turn the knob such that part (nearer to the knee) is turning to the right and part (nearer to the ankle) is turning to the left. The two knobs should be parallel with each other because they serve as the linkage for the ankle mechanism.
Take the current assembly and put it on the surface such that the back side of the knee is facing upward towards the ceiling.
Take Part and align it on the grove around the actuator hollows.
Take **4 of** M3x12 Flat bolts and lightly tighten into the holes of part to align with part .
Once part is aligned, fully tighten all **4 of** M3x12 Flat bolts to secure it to part .
Connect one connector of the 100 mm double-ended XT30 (2+2) cable to the port on left ankle A EC-A4310-P2-36 actuator (ID 5) and the other connector to the port on left knee EC-A4315-P2-36 actuator (ID 4).
# Step 8: Assemble the Knee Housing
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------- | --------: |
| Thigh assembly from Step 7 | 1 |
| | 1 |
| | 1 |
| | 1 |
| M3x10 Flat bolt | 18 |
| M3x18 Socket bolt | 6 |
| Threadlocker | As needed |
Instructions [#instructions]
Take the thigh assembly from Step 7 and lay it flat on the surface such that the L-shape part is facing upward toward the ceiling.
Take part and oriented it such that the knee cap is on top of the L-shape piece of part .
Slot the thigh assembly into part such that the two actuator holes are aligned with the knee portion first to help us estimate how the part will fit together.
Turn the leg toward the right so we can see the part of the thigh assembly with the bearing on the actuator facing upward toward the ceiling. Take part and fit it on to the groove on part , such that the protrusion fits into the bearing and then all the holes on part align with the threaded insert on part .
Take **9 of** M3x10 Flat bolts and lightly tighten into all the holes on part to align it with part . After the parts are aligned:
* remove **9 of** M3x10 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part .
Flip the current assembly 180 degrees to the other side such that part is now facing downward toward the surface. You should be able to see the actuator shaft facing toward the ceiling.
Take **2 of** M3x18 Socket bolts and lightly tighten into the threaded holes on the actuator shaft. Make sure that the bolts are on the opposite side of each other, use those bolts as the leverage to turn the actuator shaft so that the bolts is on a straight line with the rest of part . This makes it easier to align with part later on. Remove **2 of** M3x18 Socket bolts after you are done.
Align part with the actuator such that the 6 holes nearer to the center of part aligned with the threaded holes on the actuator.
Reuse **2 of** M3x18 Socket bolts removed earlier and add **4** more, for **6** total. Lightly tighten them into the 6 holes near the center of part to align the part with the actuator and the groove on part .
After the parts are aligned:
* remove **6 of** M3x18 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take **9 of** M3x10 Flat bolts and lightly tighten into all the holes on part to align it with part . After the parts are aligned:
* remove **9 of** M3x10 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part .
# Step 9: Install Left Ankle A and B Actuators
Parts needed [#parts-needed]
| Part | Count |
| --------------------------------------------------------------------------------- | --------: |
| Current assembly from Step 8 | 1 |
| Left ankle A EC-A4310-P2-36 actuator (ID 5) | 1 |
| Left ankle B EC-A4310-P2-36 actuator (ID 6) | 1 |
| 100 mm double-ended XT30 (2+2) cable | 1 |
| M3x12 Flat bolt | 12 |
| Threadlocker | As needed |
Instructions [#instructions]
Flip the current assembly over such that the knee part is facing downward toward the surface and the 2 hollow portions of part are facing upward toward the ceiling.
Place left ankle A EC-A4310-P2-36 actuator (ID 5) and left ankle B EC-A4310-P2-36 actuator (ID 6) on a flat surface with their shafts facing downward and ports near the top. Connect one connector of the 100 mm double-ended XT30 (2+2) cable to the port on ankle A and the other connector to the port on ankle B.
Take left ankle A actuator (ID 5) and align it into the first hollow nearer to the knee on part . The actuator shaft should be facing downward toward the floor. Make sure that the port and cable are aligned with the joint port cut-out between the two hollow portions.
You should be able to see the LED cut-outs on the calf part . Make sure that the actuator is oriented correctly, so that the LED portion of the actuator lines up with those cut-outs.
Take left ankle B actuator (ID 6) and align it into the second hollow nearer to the ankle on part . Make sure that the cable and ports of the actuator are on top of the cable from left ankle A actuator (ID 5) in the joint port cut out.
You should be able to see the LED cut-outs on the calf part . Make sure that the actuator is oriented correctly, so that the LED portion of the actuator lines up with those cut-outs.
Bend the knee upward such that the ankle is facing the ceiling slowly. You should be seeing the 2 actuator shafts facing you right now.
Take **6 of** M3x12 Flat bolts and lightly tighten them through the 6 holes around the first hollow near to the knee on part to align part with the actuator underneath.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take **6 of** M3x12 Flat bolts and lightly tighten them through the 6 holes around the second hollow near to the ankle on part to align part with the actuator underneath.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
# Step 1: Set Up Actuator ID 3
Parts needed [#parts-needed]
| Part | Count |
| ---------------------------------------------------------------------- | ----: |
| EC-A3814-H14-107 actuator (ID 3) | 1 |
| 280 mm single-ended XT30 (2+2) cable | 1 |
| 150 mm double-ended XT30 (2+2) cable | 1 |
Instructions [#instructions]
Place the EC-A3814-H14-107 actuator (ID 3) on a flat surface with the shaft facing downward toward the floor.
Connect the connector of the 280 mm single-ended XT30 (2+2) cable to the actuator port nearest the center hole, then route the cable through the hollow shaft.
Connect one connector of the 150 mm double-ended XT30 (2+2) cable to the outer actuator port, leaving the cable and its free connector accessible.
# Step 2: Install Actuator ID 3
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------------------------------------------------------------------------- | --------: |
| EC-A3814-H14-107 actuator (ID 3) with connected cables from Step 1 | 1 |
| | 1 |
| M4x10 Socket bolt | 8 |
| Threadlocker | As needed |
Instructions [#instructions]
Place part flat on the surface such that the surface with an L shape groove is pointing upward toward the ceiling. The long hollow shaft should be facing toward you.
The hollow shaft contains a cable channel. Take the free connector of the double-ended XT30 (2+2) cable and route the cable through the channel to the other side.
Take the EC-A3814-H14-107 actuator (ID 3) and align its port with the cable channel. Then slowly slide the actuator inside the hollow shaft until all the threaded holes on the actuator shell align with the holes on part .
Take **8 of** M4x10 Socket bolts and lightly tighten them into the holes on part to align with the actuator.
After the parts are aligned:
* remove **8 of** M4x10 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
# Step 3: Install Actuator ID 4
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------------------------------------------------------------------------------------ | --------: |
| Assembly from Step 2 containing part 600\_03C and EC-A3814-H14-107 actuator (ID 3) | 1 |
| EC-A4315-P2-36 actuator (ID 4) | 1 |
| | 1 |
| M4x14 Flat bolt | 6 |
| M3x20 Socket bolt | 1 |
| Threadlocker | As needed |
Instructions [#instructions]
Take part and lay it flat on a surface such that the hollow part is facing upward toward the ceiling.
Take the EC-A4315-P2-36 actuator (ID 4) and align the actuator port with the port cutout on part . Then, slowly lower the actuator down into the slot. Put the assembly to one side after done.
Since this part has very precise tolerance, you need to align it perfectly so that the actuator and the part fit together smoothly. The actuator needs to bottom out and align properly with part or else you will have a fitting problem later on.
Turn Part from previous step around such that the L shaped grove is still facing upward toward the ceiling and the actuator is facing away from you.
Take the XT30 (2+2) connector that is routed through the trench of part and connect it to the port on actuator (ID 4).
Take actuator (ID 4) and part and slide it through the round hole on part such that the port cut out from part aligned with the L shape cut out on part .
Take **6 of** M4x14 Flat bolts and lightly tighten into the 6 holes on part to align it with part .
After the parts are aligned:
* remove **6 of** M4x14 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Take **1 of** M3x20 Socket bolt , apply threadlocker, and tighten it into the singular hole at the very edge of the circumference of part to secure it to part .
# Step 4: Attach Part 600_06A
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------- | --------: |
| Current assembly from Step 3 | 1 |
| | 1 |
| M4x14 Flat bolt | 6 |
| M3x12 Flat bolt | 6 |
| M3x20 Socket bolt | 1 |
| Threadlocker | As needed |
Instructions [#instructions]
Lay the current assembly flat such that the part is facing downward toward the surface and the actuator shaft is facing towards the ceiling.
Take part and align it with the actuator and part such that the actuator fits into the round hollow in part and the 6 holes on the handle align with part threaded insert.
Take **6 of** M3x12 Flat bolts and lightly tighten into the 6 holes along the circumference of the round portion of part that is fitting on to the actuator to align the parts.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure part to the actuator.
Take **6 of** M4x14 Flat bolts and lightly tighten into the 6 holes on the long portion of part that align with the threaded insert on part to align the two parts together.
After the parts are aligned:
* remove **6 of** M4x14 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure part to part .
Take **1 of** M3x20 Socket bolt , apply threadlocker and tighten it into the singular hole at the very edge of the circumference of part to secure it to part .
# Step 5: Attach Part 600_12A
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------- | --------: |
| Current assembly from Step 4 | 1 |
| | 1 |
| M3x6 Socket bolt | 2 |
| Threadlocker | As needed |
Instructions [#instructions]
Turn the current assembly around such that the L-shape grove is facing upward toward the ceiling and toward you.
Take part and align it with the L-shape grove on part .
Take **2 of** M3x6 Socket bolts and lightly tighten them into the 2 holes on part to align it with part .
After the parts are aligned:
* remove **2 of** M3x6 Socket bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
# Step 6: Install the Outer Housing
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------- | ----: |
| Current assembly from Step 5 | 1 |
| | 1 |
| | 1 |
| M4x14 Socket bolt | 17 |
Instructions [#instructions]
Take the current assembly and lay it flat on the surface such that part is facing upward toward the ceiling and toward you.
Take part (it should have 8 holes, not to confuse with part ) and prepare to align to as shown below.
Take **8 of** M4x14 Socket bolts and lightly tighten them into the holes on parts to align the part to the threaded insert on part .
Take part (it should have 9 holes) and prepare to align to as shown below.
Take **9 of** M4x14 Socket bolts and lightly tighten them into the holes on parts to align the part to the threaded insert on part .
Once both parts are aligned, fully tighten all **17 of** M4x14 Socket bolts in a star formation to secure part and part to part .
# Step 7: Attach the Actuator Hub
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------- | --------: |
| Current assembly from Step 6 | 1 |
| | 1 |
| M4x25 Socket bolt | 5 |
| Threadlocker | As needed |
| Super glue | As needed |
Instructions [#instructions]
Take the current assembly and turn it such that the EC-A3814-H14-107 actuator (ID 3) is facing toward you.
Take part and locate the inner part that is not hollow. Put **5 of** M4x25 Socket bolts into the 5 holes on part .
Route the cable from the hollow shaft through the center hole in part , then through the L-shaped cable channel until it exits the other side.
Take the super glue and apply one drip at the place shown in the images below to keep the cable flush. This is required before installing the leg to the pelvis.
Align all the M4x25 Socket bolts with the threaded holes on the actuator and then lightly tighten the bolts into the actuator to align part with the actuator.
After the parts are aligned:
* remove **5 of** M4x25 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
# Step 7: Attach the Right Leg Module to the Pelvis
Parts needed [#parts-needed]
| Part | Count |
| ---------------------------------------------------------------------------------------------------------------- | --------: |
| Pelvis assembly containing right hip roll EC-A5013-H17-100 actuator (ID 8) | 1 |
| Completed Right Leg Module | 1 |
| M5x25 Socket bolt | 5 |
| Threadlocker | As needed |
| Approved soldering and insulation supplies | As needed |
Instructions [#instructions]
Take the Right Leg Module and align part with the EC-A5013-H17-100 actuator (ID 8).
Twist the Right Leg body such that you can see the 5 holes on part clearly and make sure it is aligned with the holes on the actuator shaft.
Solder each corresponding conductor from the cable leads of right hip roll actuator (ID 8) to the cable leads of right hip yaw EC-A3814-H14-107 actuator (ID 9), following the released pinout.
Follow the video below for the soldering process to make sure that the connection is made correctly else it can cause signal infidelity in the system.
Take **5 of** M5x25 Socket bolts and lightly tighten them into the 5 holes on part to align with the right hip roll actuator (ID 8).
After the parts are aligned:
* remove **5 of** M5x25 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Twist the Right leg body back to its original position so that the leg is straight and front facing.
# Step 8: Attach the Left Leg Module to the Pelvis
Parts needed [#parts-needed]
| Part | Count |
| ----------------------------------------------------------------------------------------------------------------------------- | --------: |
| Pelvis and right-leg assembly containing left hip roll EC-A5013-H17-100 actuator (ID 2) | 1 |
| Completed Left Leg Module | 1 |
| M5x25 Socket bolt | 5 |
| Threadlocker | As needed |
| Approved soldering and insulation supplies | As needed |
Instructions [#instructions]
Take the Left Leg Module and align part with the EC-A5013-H17-100 actuator (ID 2).
Twist the Left Leg body such that you can see the 5 holes on part clearly and make sure it is aligned with the holes on the actuator shaft.
Solder each corresponding conductor from the cable leads of left hip roll actuator (ID 2) to the cable leads of left hip yaw EC-A3814-H14-107 actuator (ID 3), following the released pinout.
Follow the video below for the soldering process to make sure that the connection is made correctly else it can cause signal infidelity in the system.
Take **5 of** M5x25 Socket bolts and lightly tighten them into the 5 holes on part to align with the left hip roll actuator (ID 2).
After the parts are aligned:
* remove **5 of** M5x25 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Twist the Left leg body back to its original position so that the leg is straight and front facing.
# Step 9: Complete the Pelvis Module
Congratulation! You have finished the assembly of the Pelvis module and also the full lower body of your Asimov!
We encourage you to share your progress with your friends and loved one, and with our community!
If you do, please feel free tagging us, we would love to give you a shoutout! It is a momentous event for both you and us!
# Step 1: Install the Waist Yaw Actuator
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------------------------------------------------------ | --------: |
| Paper tape | As needed |
| EC-A6416-P2-25 actuator (ID 23) | 1 |
| | 1 |
| M4x10 Flat bolt | 8 |
| | 1 |
| M4 square nuts | 8 |
| M4x18 Socket bolt | 8 |
| Threadlocker | As needed |
Instructions [#instructions]
Take EC-A6416-P2-25 actuator (ID 23) and lay it flat on a surface such that the actuator shaft is facing upward toward the ceiling, the port should be nearer to the bottom.
Take part and align the 8 holes in the inner circumference of it with the 8 threaded holes on the actuator casing. Make sure that the port cut out on part is 45 degree away to the right of one of the cable ports at the bottom.
Take **8 of** M4x10 Flat bolts , lightly tighten into the 8 inner holes of part to align the part with the actuator.
After the parts are aligned:
* remove **8 of** M4x10 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take part and put it on the surface such that the hollow part points upward towards the ceiling and the two side round holes for the hip pitch actuator are pointing to the left and right sides and not facing down. Make sure the solid part faces towards you and the rectangular hole faces away from you.
Take **8 of** M4 square nuts and put them into the 8 slots around the holes in the inner circumference of Part . You can use paper tape to help you secure the nuts for ease of moving.
Align part and the actuator such that the port cut out and the port on the actuator is pointing out toward the rectangular cut out. Make sure that the 8 outer holes are aligned with the holes on the surface of Part .
Take **8 of** M4x18 Socket bolts , lightly tighten into the 8 outer holes of part to align the part with part . Once the part is aligned, tighten each of the **8 bolts** to secure the part together.
# Step 2: Install the Left Hip Pitch Actuator
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------------------------------------------------------------- | --------: |
| Pelvis assembly from Step 1 | 1 |
| EC-A6416-P2-25 actuator (ID 1) | 1 |
| | 1 |
| M4x10 Flat bolt | 6 |
| M4 nylon-insert locknuts | 8 |
| M4x16 Flat bolt | 8 |
| 200 mm double-ended XT30 (2+2) cable | 1 |
| Threadlocker | As needed |
Instructions [#instructions]
Take EC-A6416-P2-25 actuator (ID 1) and lay it flat on a surface such that the actuator shaft is facing upward toward the ceiling.
Take part and align the 6 holes in the inner circumference of it with the 6 holes on the actuator shaft. Make sure that the port cut out on part is 30 degrees to the right from the port at the bottom.
Take **6 of** M4x10 Flat bolts , lightly tighten into the 6 inner holes of part to align the part with the actuator.
After the parts are aligned:
* remove **6 of** M4x10 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Connect one connector of the 200 mm double-ended XT30 (2+2) cable to the actuator port shown above.
Take part and lay them flat on the side, such that the left hip pitch hollow is facing upward toward the ceiling. Part should be pointing towards your right, and the solid part should be facing you, not away from you.
Take **8 of** M4 nylon-insert locknuts and put them into the 8 slots around the inner circumference of Part left hip pitch hollow.
Slot the actuator inside the left hip pitch hollow of part . Slowly guide the cable through the cut-out hole on part and . At the end, slightly twist it clockwise so that the cutout on part align with part . Then, align it such that the 8 outer holes of part are aligned with the holes on the surface of Part . If you did it correctly, you should be able to see the actuator port facing outward from the front rectangular cut out.
Take **8 of** M4x16 Flat bolts , lightly tighten into the 8 outer holes of part to align the part with part .
After the parts are aligned:
* remove **8 of** M4x16 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part together.
# Step 3: Install the Right Hip Pitch Actuator
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------------------------------------------------------------- | --------: |
| Pelvis assembly from Step 2 | 1 |
| EC-A6416-P2-25 actuator (ID 7) | 1 |
| | 1 |
| M4x10 Flat bolt | 6 |
| M4 nylon-insert locknuts | 8 |
| M4x16 Flat bolt | 8 |
| 200 mm double-ended XT30 (2+2) cable | 1 |
| Threadlocker | As needed |
Instructions [#instructions]
Take EC-A6416-P2-25 actuator (ID 7) and lay it flat on a surface such that the actuator shaft is facing upward toward the ceiling.
Take part and align its inner holes with the holes on the actuator shaft as shown below. Make sure that the port cut out on part is 30 degrees to the left from the port at the bottom.
Take **6 of** M4x10 Flat bolts and lightly tighten them into the matching inner holes of part shown below to align the part with the actuator.
After the parts are aligned:
* remove **6 of** M4x10 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Connect one connector of the 200 mm double-ended XT30 (2+2) cable to the actuator port shown above.
Take part and lay them flat on the side, such that the right hip pitch hollow is facing upward toward the ceiling. Part should be pointing towards your left, and the solid part should be facing you, not away from you.
Take **8 of** M4 nylon-insert locknuts and put them into the 8 slots around the inner circumference of Part right hip pitch hollow.
Slot the actuator inside the right hip pitch hollow of part . Slowly guide the cable through the cut-out hole on part and . At the end, slightly twist it clockwise so that the cutout on part align with part . Then, align it such that the 8 outer holes of part are aligned with the holes on the surface of Part . If you did it correctly, you should be able to see the actuator port facing outward from the front rectangular cut out.
Take **8 of** M4x16 Flat bolts , lightly tighten into the 8 outer holes of part to align the part with part .
After the parts are aligned:
* remove **8 of** M4x16 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part together.
# Step 4: Route the Hip Cables and Install the Cover
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------ | --------: |
| Pelvis assembly from Step 3 | 1 |
| | 1 |
| M3x8 Flat bolt | 4 |
| 300 mm double-ended XT30 (2+2) cable | 3 |
| Masking tape | As needed |
Instructions [#instructions]
Lay the pelvis assembly from Step 3 flat on a surface such that the rectangular hole with access to the actuator port facing upward toward the ceiling.
Connect one connector of each 300 mm double-ended XT30 (2+2) cable to the corresponding waist and hip pitch actuator ports.
Route all **3 of** the 300 mm double-ended XT30 (2+2) cables through the port cut-out of part .
Label each cable with masking tape so its free connector can be matched to the corresponding port on the RPU later.
Take part and lay it over the hole to cover it up, align the holes on part with the threaded insert.
Take **4 of** M3x8 Flat bolts and secure part to part
# Step 5: Install the Right Hip Roll Actuator
Parts needed [#parts-needed]
| Part | Count |
| ---------------------------------------------------------------------- | --------: |
| Pelvis assembly from Step 4 | 1 |
| | 1 |
| | 1 |
| | 1 |
| M4x12 Socket bolt | 6 |
| M4x6 Socket bolt | 6 |
| M4x14 Socket bolt | 3 |
| M4x8 Socket bolt | 3 |
| EC-A5013-H17-100 actuator (ID 8) | 1 |
| 180 mm single-ended XT30 (2+2) cable | 1 |
| Threadlocker | As needed |
Instructions [#instructions]
Put the pelvis assembly from Step 4 on a flat surface, oriented it such that the right hip pitch actuator is facing upward toward the ceiling. Part face toward your left, and the solid part face toward you.
Take part and align it 6 holes to the 6 holes on the actuator shaft. Oriented it such that the protruding metal stopper part stays in between the larger gap of the 2 metal stoppers on part .
Take **6 of** M4x12 Socket bolts , lightly tighten into the 6 holes of part to align the part with the actuator.
After the parts are aligned:
* remove **6 of** M4x12 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take EC-A5013-H17-100 actuator (ID 8) and connect the XT30 (2+2) connector of the 180 mm single-ended XT30 (2+2) cable to the actuator port nearest the hollow shaft, then route the cable through the shaft.
Slide the actuator through part such that the holes on circumference of the actuator are aligned with the holes on the arch of part . The actuator shaft should be facing forward in the same direction as where Part is.
Connect the free connector of the right hip pitch actuator cable to the specified port on right hip roll actuator (ID 8).
Before moving on to the next step, you need to make sure that the actuator is oriented correctly. The LED on the actuator needs to be roughly positioned as shown in the visualization, as it will need to shine through the LED cut-out on the back cover. Please refer to the video below for more details.
Take **6 of** M4x6 Socket bolts and lightly tighten into the 6 holes of part to align the part with the actuator.
After the parts are aligned:
* remove **6 of** M4x6 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Lay the pelvis assembly flat on a surface such that the Part is facing upward toward the ceiling. Take part and slot the actuator (ID 8) into it such that the actuator shaft goes through the round hole on part .
Take **3 of** M4x14 Socket bolts and tighten into the 3 holes on top of part to secure the part to the actuator.
After the parts are aligned:
* remove **3 of** M4x14 Socket bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Flip the pelvis assembly upside down such that part is now facing downward. Take part and align it with part such that the three holes on part aligned with threaded insert on part .
Take **3 of** M4x8 Socket bolts and tighten into the 3 holes on part to secure the part to part .
# Step 6: Install the Left Hip Roll Actuator
Parts needed [#parts-needed]
| Part | Count |
| ---------------------------------------------------------------------- | --------: |
| Pelvis assembly from Step 5 | 1 |
| | 1 |
| | 1 |
| | 1 |
| M4x12 Socket bolt | 6 |
| M4x6 Socket bolt | 6 |
| M4x14 Socket bolt | 3 |
| M4x8 Socket bolt | 3 |
| EC-A5013-H17-100 actuator (ID 2) | 1 |
| 180 mm single-ended XT30 (2+2) cable | 1 |
| Threadlocker | As needed |
Instructions [#instructions]
Put the pelvis assembly from Step 5 on a flat surface, oriented it such that the left hip pitch actuator is facing upward toward the ceiling. Part face toward your right, and the solid part face toward you.
Take part and align it 6 holes to the 6 holes on the actuator shaft. Oriented it such that the protruding metal stopper part stays in between the larger gap of the 2 metal stoppers on part .
Take **6 of** M4x12 Socket bolts , lightly tighten into the 6 holes of part to align the part with the actuator.
After the parts are aligned:
* remove **6 of** M4x12 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take EC-A5013-H17-100 actuator (ID 2) and connect the XT30 (2+2) connector of the 180 mm single-ended XT30 (2+2) cable to the actuator port nearest the hollow shaft, then route the cable through the shaft.
Slide the actuator through part such that the holes on circumference of the actuator are aligned with the holes on the arch of part . The actuator shaft should be facing forward in the same direction as where Part is.
Connect the free connector of the left hip pitch actuator cable to the specified port on left hip roll actuator (ID 2).
Before moving on to the next step, you need to make sure that the actuator is oriented correctly. The LED on the actuator needs to be roughly positioned as shown in the visualization, as it will need to shine through the LED cut-out on the back cover. Please refer to the video below for more details.
Take **6 of** M4x6 Socket bolts and lightly tighten them into the 6 holes of part to align the part with the actuator.
After the parts are aligned:
* remove **6 of** M4x6 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure part and the actuator together.
Lay the pelvis assembly flat on a surface such that the Part is facing upward toward the ceiling. Take part and slot the actuator (ID 2) into it such that the actuator shaft goes through the round hole on part .
Take **3 of** M4x14 Socket bolts and tighten into the 3 holes on top of part to secure the part to the actuator.
After the parts are aligned:
* remove **3 of** M4x14 Socket bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Flip the pelvis assembly upside down such that part is now facing downward. Take part and align it with part such that the three holes on part aligned with threaded insert on part .
Take **3 of** M4x8 Socket bolts and tighten into the 3 holes on part to secure the part to part .
# Step 14: Complete the Right Arm Module
Congratulations! You have finished assembling the Right Arm module and can keep it ready for torso installation.
We encourage you to share your progress with your friends and loved one, and with our community!
If you do, please feel free tagging us, we would love to give you a shoutout! It is a momentous event for both you and us!
# Step 10: Attach the Elbow Housing Plates
Parts needed [#parts-needed]
| Part | Count |
| ----------------------------------------------- | --------: |
| Elbow actuator-and-housing assembly from Step 9 | 1 |
| | 1 |
| | 1 |
| M4x16 Flat bolt | 10 |
| M3x12 Flat bolt | 6 |
| Threadlocker | As needed |
Instructions [#instructions]
Take part and align all the 5 holes on the part to fit the threaded insert on part . It should cover the actuator bottom near the port, not the actuator shaft.
Take **5 of** M4x16 Flat bolts and lightly tighten into 5 holes on part to align with part .
Flip the assembly over such that the actuator shaft is not facing toward the ceiling.
Take part and align all the 5 holes on the part to fit the threaded insert on part .
Take **6 of** M3x12 Flat bolts and lightly tighten into 6 holes on part to align with the threaded holes on the actuator.
Take **5 of** M4x16 Flat bolts and lightly tighten into 5 holes on part to align with part .
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
After the parts are aligned:
* remove **5 of** M4x16 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Flip the assembly over and repeat for the **5 bolts** on part from Instruction 2:
* remove **5 of** M4x16 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
# Step 11: Attach the Elbow Shaft Plate and Actuator Housing
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------ | --------: |
| Assembly from Step 10 | 1 |
| | 1 |
| | 1 |
| M3x16 Flat bolt | 6 |
| M4x10 Flat bolt | 3 |
| Threadlocker | As needed |
Instructions [#instructions]
Take the assembly from Step 10 and lay it flat on a surface such that the actuator is facing upward toward the ceiling.
Take part and align the 6 holes on it with with the actuator shaft.
Take **6 of** M3x16 Flat bolts and lightly tighten into the 6 holes on part to align with the actuator shaft.
Take part and rotate it such that the port cut out on it is facing toward you. Align the 3 holes on part with the 3 holes on part .
Take **3 of** M4x10 Flat bolts and lightly tighten into the 3 holes on part to align with part .
After the parts are aligned:
* remove **6 of** M3x16 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
After the parts are aligned:
* remove **3 of** M4x10 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
# Step 12: Install and Connect the Wrist Actuator
Parts needed [#parts-needed]
| Part | Count |
| --------------------------------------------------------------------- | --------: |
| Assembly from Step 11 | 1 |
| | 1 |
| | 1 |
| | 1 |
| EC-A4310-P2-36 actuator (ID 22) | 1 |
| M4x10 Flat bolt | 3 |
| M3x12 Flat bolt | 16 |
| Threadlocker | As needed |
Instructions [#instructions]
Take assembly from Step 11 and flip it around such that part with the bearing is facing the ceiling. Take part , align the protrusion with the bearing on part and align the 3 holes on it with part .
Take **3 of** M4x10 Flat bolts and lightly tighten into the 3 holes on part to align with part . After the parts are aligned:
* remove **3 of** M4x10 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Take the EC-A4310-P2-36 actuator (ID 22), align the port with the port cut-out on part , then slot it into the hollow part.
Take part and align it 4 holes at 4 corners with the 4 threaded holes on part . Also, align the 6 holes inner holes on the part with the threaded holes on the actuator shell.
Take **10 of** M3x12 Flat bolts and lightly tighten into the 10 holes on part to align with and the actuator.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts from the inner holes one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
After the parts are aligned:
* remove **4 of** M3x12 Flat bolts from the corner holes one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Take part and align the 6 holes on it with the 6 threaded holes on the actuator shaft.
Take **6 of** M3x12 Flat bolts and lightly tighten into the 6 holes on part to align with the actuator.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Connect the free connector of the double-ended XT30 (2+2) cable from the EC-A4310-P2-36 actuator (ID 21) to the port on actuator (ID 22).
# Step 9: Install the Elbow Actuator and Route Its Cables
Parts needed [#parts-needed]
| Part | Count |
| --------------------------------------------------------------------- | ----: |
| EC-A4310-P2-36 actuator (ID 21) | 1 |
| 100 mm single-ended XT30 (2+2) cable | 1 |
| 230 mm double-ended XT30 (2+2) cable | 1 |
| | 1 |
Instructions [#instructions]
Connect the XT30 (2+2) connector of the 100 mm single-ended XT30 (2+2) cable to one port on the EC-A4310-P2-36 actuator (ID 21) and one connector of the 230 mm double-ended XT30 (2+2) cable to the other actuator port. Put the assembly aside.
Take part and lay it flat on the surface. Make sure that the port cut out on the body is facing upward toward the ceiling. And the port cut out on the side is facing toward you.
Align the actuator with the round hole on part . Route the single-ended XT30 (2+2) cable through the hollow body and the double-ended XT30 (2+2) cable through the cable channel until its free connector exits the side cutout. Make sure that the LED on the actuator align with the LED cut out (above the cable port cut out) on part also.
# Step 7: Connect and Join the Shoulder Assemblies
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------ | --------: |
| Shoulder assembly from Step 2 | 1 |
| Upper arm assembly from Step 6 | 1 |
| 100 mm single-ended XT30 (2+2) cable | 1 |
| | 1 |
| | 1 |
| M4x12 Flat bolt | 8 |
| M3x16 Flat bolt | 6 |
| Threadlocker | As needed |
| Approved soldering and insulation supplies | As needed |
Instructions [#instructions]
Connect the XT30 (2+2) connector of the 100 mm single-ended XT30 (2+2) cable to the port on the shoulder roll EC-A4315-P2-36 actuator (ID 19) in the upper arm assembly.
Solder each corresponding conductor from the cable leads routed from the EC-A5013-H17-100 actuator (ID 18) to the cable leads connected to actuator (ID 19), following the released pinout.
Follow the video below for the soldering process to make sure that the connection is made correctly else it can cause signal infidelity in the system.
Lay the upper arm assembly on its side such that the part with the bearing faces upward.
Lay the shoulder assembly on its side such that the holes on part face upward. This part is symmetrical so any side is fine.
Align the protruded portion of part with the bearing, then align the 4 holes arranged in a moon shape with part from the shoulder assembly.
Take **4 of** M4x12 Flat bolts and lightly tighten into the 4 holes on part to align the part with part
After the parts are aligned:
* remove **4 of** M4x12 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Now flip the entire assembly to the opposite side, with the actuator shaft on the upper arm assembly facing upward.
Align the 6 circular holes on part with the 6 threaded holes on the actuator shaft, then align the 4 moon-shaped holes with the 4 threaded holes on part .
Take **6 of** M3x16 Flat bolts and then lightly tighten them in the 6 holes arrange in a circle on Part to the actuator shaft.
After the parts are aligned:
* remove **6 of** M3x16 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take **4 of** M4x12 Flat bolts and lightly tighten into the 4 holes arrange in a moon shape on part to align with part .
After the parts are aligned:
* remove **4 of** M4x12 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
# Step 8: Attach the Shoulder Yaw Joint Plate and Shaft Mount
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------- | --------: |
| Current shoulder assembly from Step 7 | 1 |
| | 1 |
| | 1 |
| M3x12 Flat bolt | 8 |
| M4x20 Socket bolt | 6 |
| Threadlocker | As needed |
Instructions [#instructions]
Now move Upper Arm Assembly upward so that the shaft of the Right Shoulder Yaw EC-A3814-H14-107 actuator (ID 20) is facing the ceiling.
Take part and align it with the holes on the surface of Upper Arm Assembly around the actuator shaft. Part has 1 protrusion on its edge, it is important that this protrusion is facing toward you like in the visualization. Make sure this is positioned correctly, else your arm will be locked wrongly.
Take **8 of** M3x12 Flat bolts and lightly tighten it into the 8 holes on Part to align with the threaded holes on the actuator.
After the parts are aligned:
* remove **8 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take Part and align the 6 holes on the part with the actuator shaft holes. Also route the cable form the hollow shaft of the actuator through the center of part .
Take **6 of** M4x20 Socket bolts and lightly tighten each one into the holes to align part with the actuator.
After the parts are aligned:
* remove **6 of** M4x20 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
# Step 1: Mount the Shoulder Actuator
Parts needed [#parts-needed]
| Part | Count |
| ----------------------------------------------------------------------- | --------: |
| EC-A5013-H17-100 actuator (ID 18) | 1 |
| | 1 |
| M4x8 Flat bolt | 12 |
| Threadlocker | As needed |
Instructions [#instructions]
Put the EC-A5013-H17-100 actuator (ID 18) on a flat surface, the port should be at the bottom, and the actuator shaft should be upward toward the ceiling.
Take Part and put the actuator through the center of the part, and align the holes on the actuator with the holes around the circumference of the Part .
Using **12 of** M4x8 Flat bolts , tighten each of them lightly into the holes, to keep the actuator and Part aligned first.
After the parts are aligned:
* remove **12 of** M4x8 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
# Step 2: Attach the Shoulder Base and Route the Cable
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------------------------------------------------------------------------------------- | --------: |
| Shoulder actuator assembly containing EC-A5013-H17-100 actuator (ID 18) from Step 1 | 1 |
| | 1 |
| M5x25 Socket bolt | 6 |
| 180 mm single-ended XT30 (2+2) cable | 1 |
| Threadlocker | As needed |
Instructions [#instructions]
Take part and align it with the EC-A5013-H17-100 actuator (ID 18), with the round center facing upward, and the four corner leg facing towrad the actuator shaft. Make sure that the 6 holes on part align with the threaded holes on the actuator shaft.
Take **6 of** M5x25 Socket bolts and lightly tighten into the 6 holes that you see on part to align the part with the actuator.
After the parts are aligned:
* remove **6 of** M5x25 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Connect the XT30 (2+2) connector of the 180 mm single-ended XT30 (2+2) cable to the actuator port nearest the center, then route the cable leads through the hollow shaft.
# Step 13: Connect the Shoulder and Elbow Assemblies
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------- | --------: |
| Shoulder and upper arm assembly from Step 8 | 1 |
| Forearm assembly from Step 12 | 1 |
| WAGO connector | 4 |
| M4x12 Socket bolt | 6 |
| M4x30 Socket bolt | 12 |
| Threadlocker | As needed |
Instructions [#instructions]
Position the shoulder and upper arm assembly and the forearm assembly so the color-coded cable leads from the EC-A3814-H14-107 actuator (ID 20) and the EC-A4310-P2-36 actuator (ID 21) are accessible. Insert each corresponding conductor pair into four WAGO connectors according to the released pinout, inspect each connection, and place the joined cable leads inside part .
If you have never worked with a WAGO connector before, follow the video below to strip, insert, and check each wire before continuing.
The shoulder and upper arm assembly and the forearm assembly should be connected to each other as follows. The orientation of the forearm assembly is very important as it will dictate the rotation limit of the arm, so make sure you oriented it exactly as shown below.
Fit the forearm assembly onto the shoulder and upper arm assembly such that the 6 holes on circumference of part is aligned with the 6 holes on circumference of part .
Take **6 of** M4x12 Socket bolts and lightly tighten into the 6 holes on the circumference of part to align with part
After the parts are aligned:
* remove **6 of** M4x12 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Take **12 of** M4x30 Socket bolts and insert it into the 12 holes on part . This will be used later to install the right arm module to the torso module. If the shoulder part is blocking you from insert the bolt, use a bit of force to rotate the actuator around so you can slot the bolt in.
# Step 3: Prepare the Bearing Housing
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------------------------------------- | ----: |
| | 1 |
| M4 square nuts | 3 |
| M4x16 Socket bolt | 3 |
| Flanged ball bearing 4390N159 | 1 |
Instructions [#instructions]
Take part (the part with a side cable channel, not part ) and place it on a flat surface with the inner hollow facing upward.
Place **3 of** M4 square nuts into the 3 slots on the surface of Part .
Using **3 of** M4x16 Socket bolts and lightly tighten it on the circumference of Part where you have previously inserted the square nuts to secure the square nuts in place first. If you over tighten, it will prevent the actuator from fitting in later on.
Place the flanged ball bearing 4390N159 into the bearing hole on Part .
# Step 4: Prepare the Actuator Housing
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------------------------------------- | ----: |
| | 1 |
| M4 square nuts | 7 |
| M4x16 Socket bolt | 3 |
| M4x25 Socket bolt | 4 |
Instructions [#instructions]
Take part , put it on a flat surface with the inner hollow part facing upward
Put **3 of** M4 square nuts into the 3 slots on the surface of Part
Using **3 of** M4x16 Socket bolts and lightly tighten it on the circumference of Part where you have previously inserted the square nuts to secure the square nuts in place first. If you overtighten, the actuator will not fit in later on.
Put **4 of** M4 square nuts into the 4 slots on the round circumference of Part
Using **4 of** M4x25 Socket bolts and lightly tighten it on the circumference of Part where you have previously inserted the 4 square nuts to secure the square nuts in place first.
# Step 5: Connect the Shoulder Cable Connectors and Position the Actuators
Parts needed [#parts-needed]
| Part | Count |
| ----------------------------------------------------------------------- | --------: |
| Actuator housing from Step 4 | 1 |
| EC-A4315-P2-36 actuator (ID 19) | 1 |
| EC-A3814-H14-107 actuator (ID 20) | 1 |
| 100 mm double-ended XT30 (2+2) cable | 1 |
| 180 mm single-ended XT30 (2+2) cable | 1 |
| M4x8 Socket bolt | 3 |
| M3x12 Flat bolt | 6 |
| Threadlocker | As needed |
Instructions [#instructions]
Take part from Step 4 and put it flat on a surface, with the inner hollow part facing upward.
Place the EC-A4315-P2-36 actuator (ID 19) into the round hollow. The ports on the actuator should be near to the top, and the shaft facing the bottom. The two ports of the actuator should be parallel with the length of .
Connect one connector of the 100 mm double-ended XT30 (2+2) cable to the outward-facing port on actuator (ID 19) and the other connector to the outer port on the EC-A3814-H14-107 actuator (ID 20).
Connect the XT30 (2+2) connector of the 180 mm single-ended XT30 (2+2) cable to the center port on actuator (ID 20), then route the cable through the actuator's hollow shaft.
Turn the current assembly upside down.
Align the threaded holes on the shell of actuator (ID 20) with the three hole at the bottom of the long end of Part .
Take **3 of** M4x8 Socket bolts and lightly tighten into the three hole on part to align the part with the actuator.
After the parts are aligned:
* remove **3 of** M4x8 Socket bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take **6 of** M3x12 Flat bolts and lightly tighten into the 6 holes on the surfaces of Part to align the part with the actuator.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Tighten the **3 bolts** we have put in place in Step 4 Instruction 3 around the circumference of the part. Don’t over tighten it, it is just meant to secure the actuator in place.
# Step 6: Close the Shoulder Actuator Housing
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------- | --------: |
| Actuator housing assembly from Step 5 | 1 |
| Bearing housing from Step 3 | 1 |
| M4x8 Socket bolt | 3 |
| Threadlocker | As needed |
Instructions [#instructions]
Put the part (the part with the actuator secured to it) flat on a surface, such that the actuator is facing upward toward the ceiling.
Slowly remove **4 of** M4x25 Socket bolts , one bolt at a time around the round surface edge surrounding the actuator. Do it slowly so you can avoid the square nut from falling out of the slots.
Put the Part and Part together such that the actuator is well encapsulated and all the holes on the surface aligned with each other.
Take each of the **4 bolts** you just removed and then secure it through the aligned hole from Part .
Tighten the **3 bolts** we have put in place in Step 3 Instruction 3 around the circumference of the part . Don’t over tighten it, it is just meant to secure the actuator in place.
Take **3 of** M4x8 Socket bolts and lightly tighten into the three hole on the longe end of part to align the part with the actuator.
After the parts are aligned:
* remove **3 of** M4x8 Socket bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
# Step 13: Join the Ankle Assemblies
Parts needed [#parts-needed]
| Part | Count |
| ---------------------------------------------------------------------------------------- | ----: |
| Leg assembly from Step 10 | 1 |
| Foot assembly from Step 12 | 1 |
| M4 nylon-insert nuts | 2 |
| M4x25 Socket bolt | 2 |
| Ball Joint Rod End | 2 |
| 194 mm Stainless Steel Bar | 1 |
| 124 mm Stainless Steel Bar | 1 |
| M3x6 Socket bolt | 2 |
| M3 steel washer | 2 |
Instructions [#instructions]
Take the leg assembly from Step 10 and put it on the surface such that the back side of the knee is facing upward towards the ceiling and the ankle is facing toward you.
Take **2 of** M4 nylon-insert nuts and slot them into the 2 holes at the ankle part, Make sure that the white nylon part is facing each other toward the center.
Take the foot assembly from Step 12 and slot part to fit into the ankle portion on part . Make sure that the holes are aligned with the locknuts.
Take **1 of** M4x25 Socket bolt and insert it into the hole on part from the heel direction and tighten it all the way to secure the part to Part .
Turn the current assembly 180 degrees so that the knee is now facing upward toward the ceiling.
Take **1 of** M4x25 Socket bolt and insert it into the hole on part from the toe direction and tighten it all the way to secure the part to Part .
Take the 194 mm Stainless Steel Bar and the 124 mm Stainless Steel Bar and thread each one onto its Ball Joint Rod End on the foot assembly, as shown in the visualization below. You have to keep threading until you don't see any of the thread on the Ball Joint Rod Ends exposed anymore.
Please watch the attached video to know how to do this as it has significant impact to the performance of the locomotion policy of the robot.
Take **2 of** Ball Joint Rod Ends and thread each one into the top of one of the 2 bars above. Then, insert them onto part and part . Do the same thing as above to ensure you cannot see any thread left exposed on the Ball Joint Rod End.
Take **2 of** M3x6 Socket bolts and **2 of** M3 steel washers . Place each washer on each bolt separately and tighten it onto part and part to secure the 2 Ball Joint Rod Ends .
# Step 14: Complete the Right Leg Module
Congratulation! You have finished the assembly of the Right Leg Module! Both legs of your Asimov are now complete.
We encourage you to share your progress with your friends and loved one, and with our community!
If you do, please feel free tagging us, we would love to give you a shoutout! It is a momentous event for both you and us!
# Step 11: Install the Linkage Shaft
Parts needed [#parts-needed]
| Part | Count |
| ---------------------------------------------------------------------------------------------------------- | --------: |
| | 1 |
| | 1 |
| | 1 |
| 6mm D-Shaft Stainless Steel | 1 |
| 6mm Bore 1-Side, 2-Post Pillow Block | 2 |
| Ball Joint Rod End | 2 |
| Steel Set-Screw Collar | 2 |
| M4x16 Flat bolt | 11 |
| Threadlocker | As needed |
Instructions [#instructions]
Take the 6mm D-Shaft Stainless Steel and put **2 of** Steel Set-Screw Collars onto it. Move it to the middle of the D-Shaft. Slightly tighten each set screw to keep the collar in place.
Take **2 of** Ball Joint Rod Ends and put one each on the right and left of the D-Shaft.
Take **2 of** 6mm Bore 1-Side, 2-Post Pillow Blocks and put one each on the right and left of the D-shaft. Make sure that the bearing facing outward on the Pillow Block is sunken into the hole instead of flat against it. Please view the visualization in detail to install it correctly.
Take part and fit it onto part as shown below.
Then take the D-shaft component we have been building and align it with part and according to the visualization below.
Flip the assembly upside down so part is visible. Take **4 of** M4x16 Flat bolts and lightly tighten them into the holes on part to align with the threaded holes in the two pillow-block legs.
After the parts are aligned:
* remove **4 of** M4x16 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and the 2 Pillow Block.
Take part and align it with the holes on part as shown below
Take **7 of** M4x16 Flat bolts and tighten them into the 7 holes on part as shown below to secure it to part .
After the parts are aligned:
* remove **7 of** M4x16 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Turn the current subassembly around such that part is facing downward toward the floor. Push the 2 Ball Joint Rod Ends flush against the 2 Pillow Blocks, then tighten the set screw on each Steel Set-Screw Collar to secure them in place.
# Step 12: Assemble the Foot Pivot
Parts needed [#parts-needed]
| Part | Count |
| --------------------------------------------------------------------------- | --------: |
| Foot assembly from Step 11 | 1 |
| | 1 |
| | 1 |
| 12mm Bore 1-Side, 2-Post Pillow Block | 4 |
| M4x20 Socket bolt | 4 |
| M4x16 Flat bolt | 4 |
| Threadlocker | As needed |
Instructions [#instructions]
Take part and put it on a flat surface. Oriented it such that the part is standing on the portion with the two little legs.
Take **2 of** 12mm Bore 1-Side, 2-Post Pillow Blocks and fit them into the 2 poles nearest to the top of the part. The legs of the Pillow Block should be pointing upward toward the ceiling. Make sure that the bearing facing outward on the Pillow Block is sunken into the hole instead of flat against it. Please view the visualization for the correct orientation of the bearing.
Take part and put it on top of the legs of the Pillow Block. Make sure the 4 holes on the surface of the circumference of part aligned with the Pillow Block leg.
Take **4 of** M4x20 Socket bolts and lightly tighten them into the 4 holes on the circumference of part to align it to the 2 Pillow Blocks.
After the parts are aligned:
* remove **4 of** M4x20 Socket bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and the 2 Pillow Blocks.
Now flip the current assembly upside down so that the part is facing downward toward the floor. Put the assembly to one side.
Take the assembly with part and put the remaining **2 of** 12mm Bore 1-Side, 2-Post Pillow Blocks onto the 2 poles that have not been used yet on part . Make sure that the legs of the 2 Pillow Block are facing upward toward the ceiling.
Take the foot assembly from Step 11 and try to align the 2 Pillow Block on part with part .
Take **4 of** M4x16 Flat bolts and lightly tighten into the 4 holes of part that are aligned with the 2 Pillow Block to align the parts.
After the parts are aligned:
* remove **4 of** M4x16 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and the 2 Pillow Block.
# Step 10: Install the Linkage Cranks and Cover
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------ | --------: |
| Current assembly from Step 9 | 1 |
| | 1 |
| | 1 |
| | 1 |
| M3x12 Flat bolt | 16 |
| 100 mm double-ended XT30 (2+2) cable | 1 |
| Threadlocker | As needed |
Instructions [#instructions]
Take part and align it with the threaded hole on the actuator nearer to the ankle on part .
Take **6 of** M3x12 Flat bolts and then lightly tighten them into the holes on part to align it with the threaded holes on the actuator.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take part and align it with the threaded hole on the actuator nearer to the knee on part .
Take **6 of** M3x12 Flat bolts and then lightly tighten them into the holes on part to align it with the threaded holes on the actuator.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
After part and is secured to the actuators. We need to turn the knob such that part (nearer to the knee) is turning to the left and part (nearer to the ankle) is turning to the right. The two knobs should be parallel with each other because they serve as the linkage for the ankle mechanism.
Take the current assembly and put it on the surface such that the back side of the knee is facing upward towards the ceiling.
Take Part and align it on the grove around the actuator hollows.
Take **4 of** M3x12 Flat bolts and lightly tighten into the holes of part to align with part .
Once part is aligned, fully tighten all **4 of** M3x12 Flat bolts to secure it to part .
Connect one connector of the 100 mm double-ended XT30 (2+2) cable to the port on right ankle A EC-A4310-P2-36 actuator (ID 11) and the other connector to the port on right knee EC-A4315-P2-36 actuator (ID 10).
# Step 8: Assemble the Knee Housing
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------- | --------: |
| Thigh assembly from Step 7 | 1 |
| | 1 |
| | 1 |
| | 1 |
| M3x10 Flat bolt | 18 |
| M3x18 Socket bolt | 6 |
| Threadlocker | As needed |
Instructions [#instructions]
Take the thigh assembly from Step 7 and lay it flat on the surface such that the L-shape part is facing upward toward the ceiling.
Take part and oriented it such that the knee cap is on top of the L-shape piece of part .
Slot the thigh assembly into part such that the two actuator holes are aligned with the knee portion first to help us estimate how the part will fit together.
Turn the leg toward the left so we can see the part of the thigh assembly with the bearing on the actuator facing upward toward the ceiling. Take part and fit it on to the groove on part , such that the protrusion fits into the bearing and then all the holes on part align with the threaded insert on part .
Take **9 of** M3x10 Flat bolts and lightly tighten into all the holes on part to align it with part . After the parts are aligned:
* remove **9 of** M3x10 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part .
Flip the current assembly 180 degrees to the other side such that part is now facing downward toward the surface. You should be able to see the actuator shaft facing toward the ceiling.
Take **2 of** M3x18 Socket bolts and lightly tighten into the threaded holes on the actuator shaft. Make sure that the bolts are on the opposite side of each other, use those bolts as the leverage to turn the actuator shaft so that the bolts is on a straight line with the rest of part . This makes it easier to align with part later on. Remove **2 of** M3x18 Socket bolts after you are done.
Align part with the actuator such that the 6 holes nearer to the center of part aligned with the threaded holes on the actuator.
Reuse **2 of** M3x18 Socket bolts removed earlier and add **4** more, for **6** total. Lightly tighten them into the 6 holes near the center of part to align the part with the actuator and the groove on part .
After the parts are aligned:
* remove **6 of** M3x18 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take **9 of** M3x10 Flat bolts and lightly tighten into all the holes on part to align it with part . After the parts are aligned:
* remove **9 of** M3x10 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part .
# Step 9: Install Right Ankle A and B Actuators
Parts needed [#parts-needed]
| Part | Count |
| ----------------------------------------------------------------------------------- | --------: |
| Current assembly from Step 8 | 1 |
| Right ankle A EC-A4310-P2-36 actuator (ID 11) | 1 |
| Right ankle B EC-A4310-P2-36 actuator (ID 12) | 1 |
| 100 mm double-ended XT30 (2+2) cable | 1 |
| M3x12 Flat bolt | 12 |
| Threadlocker | As needed |
Instructions [#instructions]
Flip the current assembly over such that the knee part is facing downward toward the surface and the 2 hollow portions of part are facing upward toward the ceiling.
Place right ankle A EC-A4310-P2-36 actuator (ID 11) and right ankle B EC-A4310-P2-36 actuator (ID 12) on a flat surface with their shafts facing downward and ports near the top. Connect one connector of the 100 mm double-ended XT30 (2+2) cable to the port on ankle A and the other connector to the port on ankle B.
Take right ankle A actuator (ID 11) and align it into the first hollow nearer to the knee on part . The actuator shaft should be facing downward toward the floor. Make sure that the port and cable are aligned with the joint port cut-out between the two hollow portions.
You should be able to see the LED cut-outs on the calf part . Make sure that the actuator is oriented correctly, so that the LED portion of the actuator lines up with those cut-outs.
Take right ankle B actuator (ID 12) and align it into the second hollow nearer to the ankle on part . Make sure that the cable and ports of the actuator are on top of the cable from right ankle A actuator (ID 11) in the joint port cut out.
You should be able to see the LED cut-outs on the calf part . Make sure that the actuator is oriented correctly, so that the LED portion of the actuator lines up with those cut-outs.
Bend the knee upward such that the ankle is facing the ceiling slowly. You should be seeing the 2 actuator shafts facing you right now.
Take **6 of** M3x12 Flat bolts and lightly tighten them through the 6 holes around the first hollow near to the knee on part to align part with the actuator underneath.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take **6 of** M3x12 Flat bolts and lightly tighten them through the 6 holes around the second hollow near to the ankle on part to align part with the actuator underneath.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
# Step 1: Set Up Actuator ID 9
Parts needed [#parts-needed]
| Part | Count |
| ---------------------------------------------------------------------- | ----: |
| EC-A3814-H14-107 actuator (ID 9) | 1 |
| 280 mm single-ended XT30 (2+2) cable | 1 |
| 150 mm double-ended XT30 (2+2) cable | 1 |
Instructions [#instructions]
Place the EC-A3814-H14-107 actuator (ID 9) on a flat surface with the shaft facing downward toward the floor.
Connect the connector of the 280 mm single-ended XT30 (2+2) cable to the actuator port nearest the center hole, then route the cable through the hollow shaft.
Connect one connector of the 150 mm double-ended XT30 (2+2) cable to the outer actuator port, leaving the cable and its free connector accessible.
# Step 2: Install Actuator ID 9
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------------------------------------------------------------------------- | --------: |
| EC-A3814-H14-107 actuator (ID 9) with connected cables from Step 1 | 1 |
| | 1 |
| M4x10 Socket bolt | 8 |
| Threadlocker | As needed |
Instructions [#instructions]
Place part flat on the surface such that the surface with an L shape groove is pointing upward toward the ceiling. The long hollow shaft should be facing toward you.
The hollow shaft contains a cable channel. Take the free connector of the double-ended XT30 (2+2) cable and route the cable through the channel to the other side.
Take the EC-A3814-H14-107 actuator (ID 9) and align its port with the cable channel. Then slowly slide the actuator inside the hollow shaft until all the threaded holes on the actuator shell align with the holes on part .
Take **8 of** M4x10 Socket bolts and lightly tighten them into the holes on part to align with the actuator.
After the parts are aligned:
* remove **8 of** M4x10 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
# Step 3: Install Actuator ID 10
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------------------------------------------------------------------------------------ | --------: |
| Assembly from Step 2 containing part 500\_03C and EC-A3814-H14-107 actuator (ID 9) | 1 |
| EC-A4315-P2-36 actuator (ID 10) | 1 |
| | 1 |
| M4x14 Flat bolt | 6 |
| M3x20 Socket bolt | 1 |
| Threadlocker | As needed |
Instructions [#instructions]
Take part and lay it flat on a surface such that the hollow part is facing upward toward the ceiling.
Take the EC-A4315-P2-36 actuator (ID 10) and align the actuator port with the port cutout on part . Then, slowly lower the actuator down into the slot. Put the assembly to one side after done.
Since this part has very precise tolerance, you need to align it perfectly so that the actuator and the part fit together smoothly. The actuator needs to bottom out and align properly with part or else you will have a fitting problem later on.
Turn Part from previous step around such that the L shaped grove is still facing upward toward the ceiling and the actuator is facing away from you.
Take the XT30 (2+2) connector that is routed through the trench of part and connect it to the port on actuator (ID 10).
Take actuator (ID 10) and part and slide it through the round hole on part such that the port cut out from part aligned with the L shape cut out on part .
Take **6 of** M4x14 Flat bolts and lightly tighten into the 6 holes on part to align it with part .
After the parts are aligned:
* remove **6 of** M4x14 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Take **1 of** M3x20 Socket bolt , apply threadlocker, and tighten it into the singular hole at the very edge of the circumference of part to secure it to part .
# Step 4: Attach Part 500_06A
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------- | --------: |
| Current assembly from Step 3 | 1 |
| | 1 |
| M4x14 Flat bolt | 6 |
| M3x12 Flat bolt | 6 |
| M3x20 Socket bolt | 1 |
| Threadlocker | As needed |
Instructions [#instructions]
Lay the current assembly flat such that the part is facing downward toward the surface and the actuator shaft is facing towards the ceiling.
Take part and align it with the actuator and part such that the actuator fits into the round hollow in part and the 6 holes on the handle align with part threaded insert.
Take **6 of** M3x12 Flat bolts and lightly tighten into the 6 holes along the circumference of the round portion of part that is fitting on to the actuator to align the parts.
After the parts are aligned:
* remove **6 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure part to the actuator.
Take **6 of** M4x14 Flat bolts and lightly tighten into the 6 holes on the long portion of part that align with the threaded insert on part to align the two parts together.
After the parts are aligned:
* remove **6 of** M4x14 Flat bolts one at a time,
* apply threadlocker,
* then tighten them back to secure part to part .
Take **1 of** M3x20 Socket bolt , apply threadlocker and tighten it into the singular hole at the very edge of the circumference of part to secure it to part .
# Step 5: Attach Part 500_12A
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------- | --------: |
| Current assembly from Step 4 | 1 |
| | 1 |
| M3x6 Socket bolt | 2 |
| Threadlocker | As needed |
Instructions [#instructions]
Turn the current assembly around such that the L-shape grove is facing upward toward the ceiling and toward you.
Take part and align it with the L-shape grove on part .
Take **2 of** M3x6 Socket bolts and lightly tighten them into the 2 holes on part to align it with part .
After the parts are aligned:
* remove **2 of** M3x6 Socket bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
# Step 6: Install the Outer Housing
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------- | ----: |
| Current assembly from Step 5 | 1 |
| | 1 |
| | 1 |
| M4x14 Socket bolt | 17 |
Instructions [#instructions]
Take the current assembly and lay it flat on the surface such that part is facing upward toward the ceiling and toward you.
Take part (it should have 8 holes, not to confuse with part ) and prepare to align to as shown below.
Take **8 of** M4x14 Socket bolts and lightly tighten them into the holes on parts to align the part to the threaded insert on part .
Take part (it should have 9 holes) and prepare to align to as shown below.
Take **9 of** M4x14 Socket bolts and lightly tighten them into the holes on parts to align the part to the threaded insert on part .
Once both parts are aligned, fully tighten all **17 of** M4x14 Socket bolts in a star formation to secure part and part to part .
# Step 7: Attach the Actuator Hub
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------- | --------: |
| Current assembly from Step 6 | 1 |
| | 1 |
| M4x25 Socket bolt | 5 |
| Threadlocker | As needed |
| Super glue | As needed |
Instructions [#instructions]
Take the current assembly and turn it such that the EC-A3814-H14-107 actuator (ID 9) is facing toward you.
Take part and locate the inner part that is not hollow. Put **5 of** M4x25 Socket bolts into the 5 holes on part .
Route the cable from the hollow shaft through the center hole in part , then through the L-shaped cable channel until it exits the other side.
Take the super glue and apply one drip at the place shown in the images below to keep the cable flush. This is required before installing the leg to the pelvis.
Align all the M4x25 Socket bolts with the threaded holes on the actuator and then lightly tighten the bolts into the actuator to align part with the actuator.
After the parts are aligned:
* remove **5 of** M4x25 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
# Step 5: Attach the Arm Modules
Parts needed [#parts-needed]
| Part | Count |
| ----------------------------------------------------------------- | --------: |
| Torso assembly from Step 4 | 1 |
| Completed Left Arm Module | 1 |
| Completed Right Arm Module | 1 |
| left mounting plate | 1 |
| right mounting plate | 1 |
| M4x30 Socket bolt (carried with arm modules) | 24 |
| Threadlocker | As needed |
Instructions [#instructions]
Set up part such that the neck is facing the ceiling and the waist is facing the ground.
Take part and align it with the left arm hole from the inner side of part . Then, take the left arm module and align **12 of** M4x30 Socket bolts carried with that arm with and . Lightly tighten all the bolts to secure all the parts together.
After the parts are aligned:
* remove **12 of** M4x30 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part and the Left Arm Module together.
Take part and align it with the right arm hole from the inner side of part . Then, take the right arm module and align **12 of** M4x30 Socket bolts carried with that arm with and . Lightly tighten all the bolts to secure all the parts together.
After the parts are aligned:
* remove **12 of** M4x30 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part and the Right Arm Module together.
# Step 6: Attach the Torso to the Pelvis
Parts needed [#parts-needed]
| Part | Count |
| ---------------------------------------- | --------: |
| Torso-with-arms assembly from Step 5 | 1 |
| Completed pelvis and lower-body assembly | 1 |
| M4x20 Socket bolt | 6 |
| Threadlocker | As needed |
Instructions [#instructions]
Set up part such that the neck is facing the ceiling and the waist is facing the ground.
Take the Pelvis Module and place it in a stable position such that the pelvis surface is facing the ceiling. Then take the Torso Module and align its waist portion with the Pelvis module. Make sure that the 6 holes on waist plate are aligned with the 6 holes on EC-A6416-P2-25 actuator (ID 23) of the Pelvis module. Also, make sure that the crescent-shaped cable channel is aligned and that the pelvis cables pass through without being pinched.
Take **6 of** M4x20 Socket bolts and lightly tighten into the 6 holes on part to align the Torso Module and Pelvis Module together.
After the parts are aligned:
* remove **6 of** M4x20 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
# Step 7: Install the RPU and Connect Its Cable Connectors
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------------ | ----: |
| Integrated torso and pelvis assembly from Step 6 | 1 |
| Robot Processing Unit (RPU) | 1 |
| M3x8 Socket bolt | 4 |
| 400 mm double-ended XT30 (2+2) cable | 2 |
Instructions [#instructions]
Take the RPU and align the 4 holes with the 4 threaded insert at the the bottom of part .
Take **4 of** M3x8 Socket bolts and secure the RPU to the part
Connect each of the three pelvis cable connectors to its corresponding labeled port on the RPU as shown below.
You should be able to see the following value written next to the port:
1. W => waist
2. RL => Right Leg
3. LL => Left Leg
Connect one connector of each 400 mm double-ended XT30 (2+2) cable to the specified port on right shoulder pitch EC-A5013-H17-100 actuator (ID 18) and left shoulder pitch EC-A5013-H17-100 actuator (ID 13). Route the free connectors and the neck yaw cable connector through the side cable cutout in part , then connect each connector to its labeled port on the RPU as shown below.
You should be able to see the following value written next to the port:
1. NY => Neck Yaw
2. RA => Right Arm
3. LA => Left Arm
# Step 10: Install the Battery Assembly and Torso Covers
Parts needed [#parts-needed]
| Part | Count |
| --------------------------------------------- | ----: |
| Integrated robot assembly from Step 9 | 1 |
| Battery assembly from Battery Setup | 1 |
| front torso cover | 1 |
| rear torso cover | 1 |
| M4x20 Flat bolt | 2 |
| M4x8 Flat bolt | 16 |
Instructions [#instructions]
Focus on the inner body of part , we will now install the rest of it.
Before you connect anything, check that the battery assembly is switched off. The robot must stay powered off for the rest of the build, until [Calibrate Actuators](/asimov/1/operate/setup/calibrate-actuators).
Take the battery assembly and angle it as shown below to allow enough length for the battery power cable to reach the RPU power port.
Then, connect the battery power cable (highlighted in red) to the RPU power port and slot the battery assembly inside part as shown. Connect it now: the torso covers close over this connector later in this step.
Take **2 of** M4x20 Flat bolts and tighten into the 2 holes on the front chest of part to secure the battery assembly to the body.
Take part and put on the front chest of part .
Take **8 of** M4x8 Flat bolts and tighten into 8 holes on part to secure it to part
Turn the robot around so we can now work on the back cover.
Remove the plastic cover from the charger port as shown in the image below. The back cover will not close properly if it is left on.
Take part and put on the back of part .
Take **8 of** M4x8 Flat bolts and tighten into 8 holes on part to secure it to part
# Step 8: Install the Neck Pitch Actuator and Head Base
Parts needed [#parts-needed]
| Part | Count |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------: |
| Integrated assembly from Step 7 | 1 |
| | 1 |
| | 1 |
| Head assembly from [Head Compute Unit Setup](/asimov/1/build/assembly-preparations/compute-module-setup/head-compute-unit-setup) ( head base with head carrier) | 1 |
| EC-A4310-P2-36 actuator (ID 25) | 1 |
| 200 mm double-ended XT30 (2+2) cable, connected to neck yaw EC-A4310-P2-36 actuator (ID 24) | 1 |
| M3x12 Flat bolt | 5 |
| M3x12 Socket bolt | 2 |
| M3x20 Socket bolt | 6 |
| Threadlocker | As needed |
Instructions [#instructions]
Position Torso Assembly in a straight manner such that we can see its neck, as now we will be working on installing the head module.
Take part and align the 5 holes on it with the neck actuator shaft.
Take **5 of** M3x12 Flat bolts and lightly tighten them into the 5 holes on part
After the parts are aligned:
* remove **5 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Take EC-A4310-P2-36 actuator (ID 25) and slot it through the arch of part . Make sure that the two ports are perfectly parallel to part .
Connect the free connector of the 200 mm double-ended XT30 (2+2) cable from neck yaw EC-A4310-P2-36 actuator (ID 24) to the specified port on neck pitch actuator (ID 25).
Take part and align the 2 holes with part .
Take **2 of** M3x12 Socket bolts , apply threadlocker to each of them and tighten them into the 2 holes on part to secure it to part
Take the head assembly from [Head Compute Unit Setup](/asimov/1/build/assembly-preparations/compute-module-setup/head-compute-unit-setup) and align part with the actuator (ID 25) such that the 6 holes on the side of the part is aligned with the 6 holes on the actuator shaft. While doing this you can also routed all the wire that is exposed from the back of the head to route it through the port cutout on part .
Take **6 of** M3x20 Socket bolts and lightly tighten into the 6 holes on part to align it with the actuator.
After the parts are aligned:
* remove **6 of** M3x20 Socket bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Check that the head cables connected in Head Compute Unit Setup are still seated: the USB-A to JST-ZHR4 cable (highlighted in yellow) between the camera module and the Pi 5 , the 100 mm USB-C to USB-A cable (highlighted in purple) between the Head Board and the Pi 5 , and the 120 mm JST-XH to JST-XH cable (highlighted in green) between the Media HAT Board and the Head Board . The 1000 mm XT30-Male to XT30-Female power cable (highlighted in red) and the 300 mm single-ended JST-XH cable (highlighted in orange) from the Head Board stay unconnected at their free ends until Step 9.
# Step 9: Complete the Head Enclosure
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------: |
| Integrated assembly from Step 8 | 1 |
| top head cover | 1 |
| left head cover | 1 |
| right head cover | 1 |
| M4x10 Flat bolt | 10 |
| 1000 mm XT30-Male to XT30-Female power cable (highlighted in red), connected to the Head Board in Head Compute Unit Setup | 1 |
| 1000 mm Ethernet cable (highlighted in blue), connected to the Raspberry Pi 5 in Head Compute Unit Setup | 1 |
| 600 mm double-ended XT30 (2+2) cable | 1 |
| WAGO connector for the speaker cable | As needed |
Instructions [#instructions]
Identify the 1000 mm Ethernet cable (highlighted in blue) coming out of the back of the head, and route it down through the port cutout on part into the torso.
Connect the free connector of the 1000 mm Ethernet cable (highlighted in blue), which runs from the Pi 5 , to the specified Radxa Carrier Board port in the RPU. This is exactly the same as the step that you have done previously to assign the actuator ID.
Route the 1000 mm XT30-Male to XT30-Female power cable (highlighted in red) from the Head Board through part and the neck cutout, then connect its free connector to the specified port on the Power Distribution Board .
Connect the speaker cable from speaker to the bare end of the 300 mm single-ended JST-XH cable (highlighted in orange) from the Head Board using the WAGO connector.
Take the 600 mm double-ended XT30 (2+2) cable and plug it into neck pitch EC-A4310-P2-36 actuator (ID 25) as shown. Route it through the port cutout on part , then connect the other end to the specified port on the PDB .
Take part and slide it on top of the Pi 5 and align with part .
Take part and fit it to the left side of the head.
Take **5 of** M4x10 Flat bolts and lightly tighten into the 5 holes on part to align with the head. Once aligned, tighten all **5 bolts** to secure the part together.
Take part and fit it to the right side of the head.
Take **5 of** M4x10 Flat bolts and lightly tighten into the 5 holes on part to align with the head. Once aligned, tighten all **5 bolts** to secure the part together.
# Step 1: Install the Shoulder Hardware
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------------------------------------------------------------- | ----: |
| | 1 |
| M6 nylon-insert locknuts | 2 |
| M6x85 Socket bolt | 2 |
| Chain Shackle | 2 |
Instructions [#instructions]
Take part and flip it upside down such that the neck is facing toward the surface while the waist is pointing toward the ceiling. The hollow part should be facing you.
Inside of the hollow part, at the neck area, you should be able to locate 2 indented slots. Take **2 of** M6 nylon-insert locknuts and slot them inside those 2 indented slots. Make sure that the white part of the nylon insert is facing toward the neck area, not facing toward the shoulder.
Rotate the shell 90 degrees anti clockwise, you should be able to see the right arm hole. At the shoulder position, there should be 2 parallel slots there. Take **1 of** Chain Shackle and slot its leg into the slots. Use your hand to keep it in the slots.
Around the circumference of the arm, at the shoulder, you should see one round hole. Take **1 of** M6x85 Socket bolt and insert it all the way into the hole and tighten the bolt to help secure both the chain shackle and M6 nylon-insert locknuts in place.
Once you are done, rotate 180 degrees so that you can see the left arm hole. Repeat the same step as above.
# Step 2: Install the Waist Plate
Parts needed [#parts-needed]
| Part | Count |
| -------------------------------------------------------------------------------------------- | --------: |
| Torso assembly from Step 1 | 1 |
| waist plate | 1 |
| M6 nylon-insert locknuts | 4 |
| M6x25 Flat bolt | 4 |
| Masking tape or temporary retainer | As needed |
Instructions [#instructions]
Take part and lay the chest flat on the surface. The hollow part should be facing toward the ceiling. The waist should be pointing away from you, the neck should be facing you.
Take a look at the waist portion inside the hollow part. You should see 4 insert holes at 4 corners of the waist. Take **4 of** M6 nylon-insert locknuts and place them inside, make sure that the white part of the locknuts is facing toward you, not toward the waist.
A tip is to use masking tape or something similar to secure the locknuts in place, so they don't fall out while you move the part around.
Take part and put it down such that the neck is facing toward the surface while the waist is pointing toward the ceiling. The hollow part should be facing you.
Take part and align it with the waist surface of part .
Take **4 of** M6x25 Flat bolts and tighten in the 4 holes at 4 corners of part such that it is secured with the M6 nylon-insert locknuts before, and align part with part . You might want to use your finger to help secure the locknut in place to make it easier to tighten.
# Step 3: Install the Neck Yaw Actuator
Parts needed [#parts-needed]
| Part | Count |
| --------------------------------------------------------------------- | --------: |
| Torso assembly from Step 2 | 1 |
| EC-A4310-P2-36 actuator (ID 24) | 1 |
| | 1 |
| M3x12 Flat bolt | 4 |
| M4x12 Flat bolt | 6 |
| 200 mm double-ended XT30 (2+2) cable | 1 |
| 600 mm double-ended XT30 (2+2) cable | 1 |
| 1000 mm Ethernet cable | 1 |
| Threadlocker | As needed |
Instructions [#instructions]
Take EC-A4310-P2-36 actuator (ID 24) and lay it flat on a surface such that the actuator shaft is facing toward the ceiling, the port is at the bottom.
Take the 1000 mm Ethernet cable that previously was used to connect the Raspberry Pi 5 and the RPU and route it through part . Since the Ethernet cable connector won't be able to fit through the port cutout after installation on the actuator.
Take part and align the 4 inner holes with the threaded holes on the actuator.
Take **4 of** M3x12 Flat bolts and tighten them lightly into the 4 holes on part to align it with the actuator.
After the parts are aligned:
* remove **4 of** M3x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and the actuator together.
Connect one connector of the 200 mm double-ended XT30 (2+2) cable to the specified neck yaw actuator port, then route the cable through the cutout in part . Leave the free connector accessible for the neck pitch actuator.
Connect one connector of the 600 mm double-ended XT30 (2+2) cable to the other neck yaw actuator port and leave the free connector accessible for the RPU connection.
Take the actuator and part and align it with the neck on part . The 600 mm double-ended XT30 (2+2) cable should be routed through the neck to the inner hollow of the body. The 200 mm double-ended XT30 (2+2) cable should be routed through part and face forward toward the chest of part . Then, carefully lower down the actuator into the neck hole of part .
Take **6 of** M4x12 Flat bolts and lightly tighten into each of the holes on part to align Part with the neck portion of part .
After the parts are aligned:
* remove **6 of** M4x12 Flat bolts one at a time in a star formation,
* apply threadlocker,
* then tighten them back to secure Part and part .
# Step 4: Install the Speaker
Parts needed [#parts-needed]
| Part | Count |
| ------------------------------------------ | --------: |
| Torso assembly from Step 3 | 1 |
| speaker | 1 |
| M4x8 Socket bolt | 4 |
| 50 mm wire | 2 |
| Approved soldering and insulation supplies | As needed |
| Threadlocker | As needed |
Instructions [#instructions]
Take part and put it down such that the neck is facing upward toward the ceiling while the waist is pointing downward toward the surface. The hollow part should be facing away from you.
Take the speaker and a pair of 50 mm wires to solder them onto the speaker as shown in the video below.
Take part (the speaker) and align it with the rectangular hole in front of the chest. Then, slot the speaker in the hole.
Take **4 of** M4x8 Socket bolts and lightly tighten into the 4 corner of part to align the part with Part .
After the parts are aligned:
* remove **4 of** M4x8 Socket bolts one at a time,
* apply threadlocker,
* then tighten them back to secure Part and part together.
Take the wire from the speakers and routed it through the port cut out toward the back of part .