The Universal Robots SDK for Python enables seamless integration with UR cobots (CB-Series and e-Series) for automation, data exchange, and remote control through all native communication protocols.
Whether you're building a custom MES/SCADA integration, a monitoring dashboard, or a test harness, this SDK provides a single Pythonic API for every UR interface.
It supports communication with real robots and URSim simulators.
π More Information: https://underautomation.com/universal-robots
π Also available in π¦ .NET & π¨ LabVIEW & π§ Unity
β Star this repo if it's useful to you!
ποΈ Watch for updates
- β‘ RTDE β stream real-time robot data at up to 500 Hz (TCP pose, joint angles, forces, I/O, registers)
- π Dashboard Server β load, play, stop programs; power on/off; robot mode (Polyscope Legacy)
- π REST API β full HTTP control of robot and program state (PolyscopeX only)
- π Primary Interface β subscribe to telemetry packets; send URScript commands
- π SFTP β list, download and upload programs and files over SSH
- π¦Ύ Kinematics β offline forward & inverse kinematics for all UR models (no connection needed)
- π Socket Communication β bidirectional messaging between Python and URScript programs
- π License management β trial, registration, and status
All protocols use standard UR communication : no custom robot options required.
- Python 3.7 or higher
- A UR robot or URSim simulator
mkdir my-ur-project
cd my-ur-project
python -m venv venv
# On Windows:
venv\Scripts\activate
# On macOS/Linux:
source venv/bin/activatepip install UnderAutomation.UniversalRobotsAll dependencies (including pythonnet) are installed automatically.
On Linux, also install .NET runtime and set the PYTHONNET_RUNTIME variable:
sudo apt-get install -y dotnet-runtime-8.0
export PYTHONNET_RUNTIME=coreclrAlternative: install from source
git clone https://github.com/underautomation/UniversalRobots.py.git cd UniversalRobots.py pip install -e .
from underautomation.universal_robots.ur import UR
from underautomation.universal_robots.connect_parameters import ConnectParameters
from underautomation.universal_robots.rtde.rtde_output_data import RtdeOutputData
from underautomation.universal_robots.rtde.rtde_input_data import RtdeInputData
from underautomation.universal_robots.rtde.rtde_input_values import RtdeInputValues
robot = UR()
# Configure protocols
params = ConnectParameters("192.168.0.1")
# Dashboard Server (load/play/stop programs, robot state)
params.dashboard.enable = True
# Primary Interface (telemetry packets, URScript)
params.primary_interface.enable = True
# RTDE (real-time data at up to 500 Hz)
params.rtde.enable = True
params.rtde.frequency = 10
params.rtde.output_setup.add(RtdeOutputData.ActualTcpPose)
params.rtde.output_setup.add(RtdeOutputData.ActualQ)
params.rtde.input_setup.add(RtdeInputData.InputIntRegisters, 24)
# SFTP file transfer over SSH
params.ssh.enable = True
params.ssh.enable_sftp = True
params.ssh.username = "ur"
params.ssh.password = "easybot"
# Connect (if you get a license exception, request a free trial at https://underautomation.com/license)
robot.connect(params)
# Read live TCP pose via RTDE
@robot.rtde.output_data_received
def on_rtde(sender, event):
pose = robot.rtde.output_data_values.actual_tcp_pose
print(f"TCP: x={pose[0]:.3f} y={pose[1]:.3f} z={pose[2]:.3f}")
# Send a URScript command
robot.primary_interface.script.send('popup("Hello from Python!", blocking=False)')
# Dashboard: load and start a program
robot.dashboard.load_program("my_program.urp")
robot.dashboard.play()
# SFTP: list programs on the controller
programs = robot.sftp.enumerate_programs()
for p in programs:
print(p)
# Don't forget to disconnect
robot.disconnect()The SDK works out of the box for 30 days (trial period) : no registration needed.
After the trial, you can:
- Buy a license at underautomation.com/order
- Get a new trial period immediately by email at underautomation.com/license
To register a license in code:
from underautomation.universal_robots.ur import UR
license_info = UR.register_license("your-licensee", "your-license-key")
print(license_info)The repository includes a complete set of ready-to-run examples in the examples/ folder, organized by communication protocol.
| File | Role |
|---|---|
examples/launcher.py |
Interactive menu : browse and run any example from a single launcher |
examples/__init__.py |
Shared helpers : path setup, robot IP persistence, SSH credentials, license management |
examples/robot_config.json |
Saved settings (git-ignored) : remembers your robot IP, credentials, and license key |
Run an example directly:
# First time: will ask for robot IP and save it
python examples/dashboard/dashboard_get_robot_state.pyOr browse all examples with the interactive launcher:
python examples/launcher.pyββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β Universal Robots Python SDK - Interactive Example Launcher β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SELECT A CATEGORY β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β β
β π 1. DASHBOARD (4 examples) β
β Dashboard Server (Polyscope Legacy) - load/play/stop programs, ... β
β β
β π¦Ύ 2. KINEMATICS (1 example) β
β Kinematics - offline forward & inverse kinematics, no connection neededβ
β β
β π 3. LICENSE (1 example) β
β License management - activation & status β
β β
β π 4. PRIMARY_INTERFACE (3 examples) β
β Primary Interface - robot telemetry packets, send URScript β
β β
β π 5. REST (3 examples) β
β REST API (PolyscopeX) - robot & program control via HTTP β
β β
β β‘ 6. RTDE (2 examples) β
β RTDE - real-time data streaming up to 500 Hz, I/O & registers β
β β
β π 7. SFTP (3 examples) β
β SFTP - file transfer, list/download/upload programs β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β 0. Exit β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| # | Example | Description |
|---|---|---|
| 1 | dashboard_get_robot_state.py | Read robot mode, loaded program, program state, and Polyscope version |
| 2 | dashboard_load_play_program.py | Load a .urp program and start it remotely |
| 3 | dashboard_power_on_release_brakes.py | Power on the robot arm and release brakes from Python |
| 4 | dashboard_popup_message.py | Show a popup on the teach pendant and close it programmatically |
| # | Example | Description |
|---|---|---|
| 1 | rest_get_robot_state.py | Read current robot state and program state via HTTP |
| 2 | rest_load_play_program.py | Load and start a program via the REST API |
| 3 | rest_power_on_brake_release.py | Power on the robot and release brakes via the REST API |
| # | Example | Description |
|---|---|---|
| 1 | rtde_read_tcp_pose.py | Stream TCP pose (x,y,z,rx,ry,rz) and joint angles in real time |
| 2 | rtde_read_digital_io.py | Monitor digital input and output states with change detection |
| 3 | rtde_read_write_registers.py | Read output integer registers and write input integer registers |
| # | Example | Description |
|---|---|---|
| 1 | sftp_list_programs.py | List all .urp programs and browse the robot file system |
| 2 | sftp_download_program.py | Download a .urp program file from the controller |
| 3 | sftp_upload_program.py | Upload a local .urp program file to the controller |
| # | Example | Description |
|---|---|---|
| 1 | primary_interface_read_telemetry.py | Subscribe to robot mode, joint data, and cartesian info events |
| 2 | primary_interface_send_urscript.py | Send URScript commands directly to the robot controller |
| 3 | primary_interface_global_variables.py | Read global variables declared in the loaded Polyscope program |
| # | Example | Description |
|---|---|---|
| 1 | kinematics_forward_inverse.py | Select a UR model, compute FK from joint angles, then compute all IK solutions |
| # | Example | Description |
|---|---|---|
| 1 | license_info_example.py | Display license state, register a license, view all license properties |
RTDE is the recommended interface for high-frequency monitoring and control. It streams binary data at up to 500 Hz on port 30004 with a configurable recipe system.
What you can do:
- Subscribe to any combination of robot output variables (TCP pose, joint positions, forces, I/O states, temperatures, voltages, etc.)
- Write to robot input variables (digital outputs, analog outputs, integer/double/boolean registers)
- Stream at any frequency from 1 to 500 Hz (version 2 only)
- Use events (
output_data_received) for reactive, non-blocking data handling
Quick example:
from underautomation.universal_robots.ur import UR
from underautomation.universal_robots.connect_parameters import ConnectParameters
from underautomation.universal_robots.rtde.rtde_output_data import RtdeOutputData
from underautomation.universal_robots.rtde.rtde_input_data import RtdeInputData
from underautomation.universal_robots.rtde.rtde_input_values import RtdeInputValues
robot = UR()
params = ConnectParameters("192.168.0.1")
params.rtde.enable = True
params.rtde.frequency = 125
params.rtde.output_setup.add(RtdeOutputData.ActualTcpPose)
params.rtde.output_setup.add(RtdeOutputData.ActualTcpForce)
params.rtde.output_setup.add(RtdeOutputData.ActualDigitalOutputBits)
params.rtde.input_setup.add(RtdeInputData.InputIntRegisters, 24)
robot.connect(params)
@robot.rtde.output_data_received
def on_data(sender, event):
vals = robot.rtde.output_data_values
pose = vals.actual_tcp_pose
force = vals.actual_tcp_force
print(f"x={pose[0]:.3f} Fx={force[0]:.2f} N")
# Write back to the robot
inputs = RtdeInputValues()
inputs.input_int_registers.x24 = 42
robot.rtde.write_inputs(inputs)
robot.disconnect()The Dashboard Server runs on port 29999 and exposes a text-based command interface for program lifecycle control. Compatible with all CB-Series and e-Series robots running Polyscope.
What you can do:
- Load, play, stop, and pause programs
- Power on/off the robot arm and release brakes
- Read robot mode, safety status, and Polyscope version
- Show and close popup messages on the teach pendant
- Set operational mode (Manual / Automatic) for remote control
- Add messages to the robot controller log
Quick example:
from underautomation.universal_robots.ur import UR
from underautomation.universal_robots.connect_parameters import ConnectParameters
robot = UR()
params = ConnectParameters("192.168.0.1")
params.dashboard.enable = True
robot.connect(params)
robot.dashboard.power_on()
robot.dashboard.release_brake()
robot.dashboard.load_program("my_program.urp")
robot.dashboard.play()
mode = robot.dashboard.get_robot_mode()
print(f"Robot mode: {mode.value}")
robot.disconnect()The REST API is available on PolyscopeX robots (Polyscope version 9+) and provides a modern HTTP-based interface for robot and program control.
Quick example:
from underautomation.universal_robots.ur import UR
from underautomation.universal_robots.connect_parameters import ConnectParameters
robot = UR()
params = ConnectParameters("192.168.0.1")
params.rest.enable = True
robot.connect(params)
robot.rest.power_on()
robot.rest.brake_release()
robot.rest.load_program("my_program")
robot.rest.play()
state = robot.rest.get_program_state()
print(state.value)
robot.disconnect()The Primary Interface (port 30001) streams binary data packets from the robot at ~10 Hz and also accepts URScript commands.
What you can do:
- Subscribe to robot mode, joint data, TCP cartesian position, tool data, safety data, and more
- Send URScript programs or commands to be executed immediately
- Read global variables declared in the loaded Polyscope program
Quick example:
from underautomation.universal_robots.ur import UR
from underautomation.universal_robots.connect_parameters import ConnectParameters
robot = UR()
params = ConnectParameters("192.168.0.1")
params.primary_interface.enable = True
robot.connect(params)
@robot.primary_interface.cartesian_info_received
def on_cart(sender, event):
c = robot.primary_interface.cartesian_info
print(f"x={c.x:.3f} y={c.y:.3f} z={c.z:.3f}")
robot.primary_interface.script.send('set_digital_out(0, True)')
robot.disconnect()SFTP gives full file system access to the UR controller over SSH (port 22).
Default credentials: user=ur, password=easybot
What you can do:
- List, download, and upload files
- Enumerate all
.urpprograms and.installationfiles - Create and delete directories
Quick example:
params.ssh.enable = True
params.ssh.enable_sftp = True
params.ssh.username = "ur"
params.ssh.password = "easybot"
robot.connect(params)
programs = robot.sftp.enumerate_programs()
robot.sftp.download_file("/programs/my_prog.urp", "C:/backup/my_prog.urp")
robot.sftp.upload_file("C:/new_prog.urp", "/programs/new_prog.urp")The SDK includes an offline kinematics engine with factory DH parameters for all UR models (UR3, UR5, UR10, UR3e, UR5e, UR7e, UR10e, UR12e, UR16e, UR15, UR18, UR20, UR30). No robot connection or license required.
Quick example:
import math
from underautomation.universal_robots.kinematics.kinematics_utils import KinematicsUtils
from underautomation.universal_robots.common.robot_models_extended import RobotModelsExtended
dh = KinematicsUtils.get_dh_parameters_from_model(RobotModelsExtended.UR10e)
# Forward kinematics: joint angles β TCP pose
angles_rad = [math.radians(a) for a in [0, -90, 0, -90, 0, 0]]
result = KinematicsUtils.forward_kinematics(angles_rad, dh)
T = result.tool_transform # 4Γ4 homogeneous matrix (flat list of 16 floats)
# Inverse kinematics: TCP pose β all joint solutions
solutions = KinematicsUtils.inverse_kinematics(T, dh)
nearest = KinematicsUtils.get_nearest_solution(solutions, angles_rad)
print([math.degrees(v) for v in nearest])- Documentation & licensing: https://underautomation.com/universal-robots
- Also available in .NET, LabVIEW, and Unity
Made with β€οΈ by the UnderAutomation team.
