Skip to main content

Cyberwave is in Private Beta.

Request early access to get access to the Cyberwave dashboard.

Joint control for robot arms and any articulated twin. Joint names come from twin.joints.list(); read and write by name (not numeric index). Positions are radians by default — pass degrees=True for degrees.
For locomotion, runtime modes, and the command catalog, see Twin Control.

Read and write joints

import math

robot = cw.twin("the-robot-studio/so101")

# List controllable joint names
joint_names = robot.joints.list()

# Set one joint (radians by default)
robot.joints.set("shoulder_joint", math.pi / 4)

# Or use degrees
robot.joints.set("elbow_joint", 45, degrees=True)

# Set many at once
robot.joints.set({"shoulder_joint": math.pi / 4, "elbow_joint": 0.5})
robot.set_joints({joint_names[0]: -0.2})
Reads:
# All joints by default (radians)
all_joints = robot.get_joints()

# Subset + multiple state kinds
subset = robot.joints.get(what_joints=["shoulder_joint"], what_data=["position"])
states = robot.joints.get(what_data=["position", "velocity", "effort"])
print_joint_states() fetches the latest states from the server and prints a formatted table (radians and degrees):
robot.joints.print_joint_states()
Joint states for twin <twin-uuid>:
------------------------------------------------------
Joint                       Radians       Degrees
------------------------------------------------------
elbow_joint               0.0000 rad       0.00 °
shoulder_joint            0.7854 rad      45.00 °
------------------------------------------------------

Saved movements and poses

Replay saved poses and movements on a twin without tracking which scope owns them. run_movement sends the movement action contract, move_to_pose snaps the twin to a saved pose, and list_movements enumerates everything available.
robot = cw.twin("the-robot-studio/so101")

robot.list_movements()        # twin, asset, and environment scopes
robot.run_movement("Wave")    # plays whichever scope owns "Wave"
robot.move_to_pose("Stand")   # snaps to the saved pose by name
run_movement, move_to_pose, and list_movements default to scope="auto", so the backend resolves the name across the twin/asset/environment scopes. Pass scope="twin", scope="asset", or scope="environment" to pin the lookup.
For the full endpoint reference, see the API Reference.