Skip to main content

Universal Robots e-Series control toolkit built on ur_rtde

Project description

URKit

PyPI

URKit is a Python toolkit for Universal Robots e-Series robots that makes the common stuff simple and gets out of the way for everything else.

Built on ur_rtde, it packages the operations you reach for most: connecting, moving to named points, gripper control, telemetry, and I/O, while exposing the raw RTDE interfaces for anything deeper.


Table of Contents


Quick Start

pip install urkit

Requires Python 3.8+ and a Universal Robots e-Series (UR3e to UR30).

Robot Setup (one-time)

  1. Network: SystemNetwork: set the robot's IP and subnet.
  2. Remote Control: SystemRemote Control: Enable. Press the remote/local button on the pendant.
  3. Security: SecurityServices: enable RTDE and disable EtherNet/IP, PROFINET, or MODBUS if they're claiming RTDE registers. Save and restart.

That's it. No .urp files to run, no extra programs needed.

Robotiq Grippers (optional)

Install the Robotiq Gripper Control URCap: download from robotiq.com/support, copy the .urcap to a USB drive, mount on the robot, and install via SettingsSystemURCaps.

Hello World

from urkit import URRobot, ROBOTIQ_HAND_E

robot = URRobot(ip="172.31.1.200", points="points.db", gripper=ROBOTIQ_HAND_E)
robot.gripper.activate()

robot.move_to("home")
robot.move_to("pick", offset=[0, 0, 0.05, 0, 0, 0])
robot.gripper.close()
robot.move_to("place")
robot.gripper.open()

The typical workflow:

  1. Teach points — use the CLI to position the robot and save named waypoints.
  2. Write code — create a robot, move to points by name, apply offsets, run sequences.
  3. Iterate — add more points, tweak your code, repeat.

Interactive CLI

URKit provides two CLI tools: teach for interactive robot control, and points for browsing saved waypoints.

Teach Mode

The interactive teach pendant for moving the robot, saving points, and checking telemetry:

urkit teach 172.31.1.200              # with robot IP
urkit teach                           # reads IP from config.yaml

Flags:

Flag Description
ip Robot IP address (positional, overrides config)
--gripper Gripper preset: 2f-85, 2f-140, hand-e, digital, none
--gripper-pin Digital gripper output pin (default: 0)
--gripper-force Robotiq force 0-100
--gripper-speed Robotiq speed 0-100
--gripper-close-on-high Digital polarity: true or false
--points Path to points.db file (overrides config)
--config Path to config file (default: config.yaml in project root or CWD)
-v, --verbose Show verbose output (debug connection issues)

Points Explorer

Browse saved waypoints with real-time search filtering — no robot connection needed:

urkit points                          # uses default points.db
urkit points test_points.db           # use specific database

Features:

  • Type to search — real-time substring filtering
  • Fuzzy matching — type pk to find pick_1 (>60% match)
  • Smart sorting — exact prefix matches first, then substring, then fuzzy
  • Spatial sorting — points ordered by proximity to "home" point (XYZ distance)
  • Theme-aware — automatically adapts colors for light/dark terminals
  • Arrow keys — scroll · ESC — quit

Key Map

All movement and orientation keys support hold-to-repeat.

Movement Orientation Step Size
KeyAction
W / S+X / -X
A / D+Y / -Y
Q / E+Z / -Z
KeyAction
U / ORoll - / +
I / KPitch - / +
J / LYaw - / +
KeyAction
1Set linear step (mm)
2Set angular step (deg)
.Reset defaults (5 mm / 2°)
Gripper Points Controls
KeyAction
XOpen
CClose
VSet position (mm)
KeyAction
BSave current position
GGo to saved point
HDelete saved point
POpen points explorer
RRename saved point
KeyAction
FFreedrive (OFF → ALL → XYZ+Rz)
MToggle frame (BASE / TOOL)
NGo To mode (Cartesian / Joint)
TOrient TCP down (180°)
YSave config to file
ESCExit

API Reference

Connecting

from urkit import URRobot, ROBOTIQ_HAND_E

robot = URRobot(ip="172.31.1.200", points="points.db", gripper=ROBOTIQ_HAND_E)

With custom motion defaults:

robot = URRobot(
    ip="172.31.1.200",
    points="points.db",
    gripper=ROBOTIQ_HAND_E,
    default_vel=0.5,    # m/s
    default_acc=0.3,    # m/s²
)

From a config file:

robot = URRobot.from_config("config.yaml")
robot = URRobot.from_config("config.yaml", ip="10.0.0.50")  # override IP

Grippers

Three built-in presets:

Preset Description
ROBOTIQ_HAND_E Robotiq 2F-140-E (Hand-E series)
ROBOTIQ_2F_85 Robotiq 2F-85
ROBOTIQ_2F_140 Robotiq 2F-140
robot.gripper.activate()              # required before open/close (Robotiq only)
robot.gripper.is_activated()          # check activation state

robot.gripper.open()                  # fully open (blocking by default)
robot.gripper.close()                 # fully closed, stops on contact
robot.gripper.open(wait=False)        # non-blocking return
robot.gripper.set_position(20)        # 20mm open (Robotiq only, 0 = closed)
robot.gripper.set_force(50)           # grip force: 0-100
robot.gripper.set_speed(80)           # movement speed: 0-100

Override preset values for custom fingers:

robot = URRobot(ip="172.31.1.200", points="points.db", gripper=ROBOTIQ_HAND_E, max_mm=120)

Digital I/O Grippers

For suction cups, solenoids, or custom actuators:

from urkit import URRobot, DigitalGripperConfig

robot = URRobot(
    ip="172.31.1.200",
    points="points.db",
    gripper=DigitalGripperConfig(pin=3),
)

robot.gripper.open()    # turn pin off (release)
robot.gripper.close()   # turn pin on (grab)

Points & Motion

The points database is optional — create a robot without one and attach later:

robot = URRobot(ip="172.31.1.200")
robot.points_db = "points.db"

Moving to Points

robot.move_to("pick")                      # linear move (default)
robot.move_to("pick", linear=False)        # joint move
robot.move_to("pick", vel=1.0, acc=0.5)    # override speed

Raw Poses

robot.move_to([0.5, 0, 0.3, 0, 0, 0])     # [x, y, z, rx, ry, rz]

Offsets

robot.move_to("pick", offset=[0, 0, 0.05, 0, 0, 0])  # 5cm above pick

Coordinate Frame

from urkit import MoveFrame

robot.move_frame = MoveFrame.TOOL   # default is BASE
robot.move_to("pick", offset=[0, 0, 0.05])  # 5cm along tool Z
  • BASE (default): offset relative to robot base
  • TOOL: offset relative to TCP orientation

Point Management

robot.save_point("here")
robot.point_names()           # ["home", "pick", "place"]
robot.rename_point("old", "new")
robot.delete_point("old")
robot.export_points("backup.json")
robot.import_points("backup.json")

Relative Moves

robot.move_relative([0, 0.01, 0, 0, 0, 0])  # 1cm along Y
robot.move_relative([0, 0, 0.05], frame=MoveFrame.TOOL)

Sequences with Blending

robot.move_sequence(["a", "b", "c"])
robot.move_sequence(["a", "b", "c"], blend_radius=0.02)

Contact Detection

robot.move_until_contact([0, 0, -0.02, 0, 0, 0])  # Ctrl+C to stop

Velocity Control

robot.move_velocity([0, 0, -0.02, 0, 0, 0], duration=1.0)

Freedrive Mode

from urkit import FreedriveMode

robot.enable_freedrive()              # all 6 axes free
robot.enable_freedrive(FreedriveMode.XYZ)      # linear axes + Rz rotation
robot.enable_freedrive(FreedriveMode.ROTATION) # rotation only
robot.disable_freedrive()             # disable before sending motion commands
robot.is_freedrive_active             # check state

Speed Control

robot.speed_stop()                    # emergency stop
robot.set_speed_slider(0.5)           # 50% hardware velocity cap

The speed slider is a hardware-level multiplier — same as the physical slider on the pendant. It's global, persistent, and affects all motion commands.

Inverse Kinematics

joints = robot.inverse_kinematics([0.5, 0, 0.3, 0, 0, 0])

Telemetry

pose = robot.get_tcp_pose()           # [x, y, z, rx, ry, rz]
joints = robot.get_joint_positions()  # [j0..j5]
force = robot.get_tcp_force()         # [fx, fy, fz, mx, my, mz]
mode = robot.get_robot_mode()         # "REMOTE_CONTROL", "SERVOING", etc.
scaling = robot.get_speed_scaling()   # 0.0-1.0
payload = robot.get_payload()         # kg
robot.is_protective_stopped()         # bool
robot.is_emergency_stopped()          # bool
robot.current_point()                 # {"pose": [...], "joints": [...]}

Digital I/O

robot.set_digital_output(0, True)
robot.set_digital_outputs({0: True, 1: False, 8: True})
robot.set_digital_outputs(False)      # clear all

robot.get_digital_input(0)
robot.get_analog_input(0)
robot.get_tool_input(0)

robot.wait_for_input(0, True, timeout=10.0)  # block until pin 0 goes high

Configuration

URKit uses a YAML config file (config.yaml) to persist settings between sessions.

Location

URKit searches for config.yaml in this order:

  1. Explicit path via --config flag or load_config("path")
  2. Project root (where src/urkit lives)
  3. Current working directory

Keys

Key Description Example
robot_ip Robot IP address 192.168.1.100
points_path Path to SQLite points database points.db
gripper Gripper preset name hand-e, 2f-85, 2f-140, digital
default_vel Default linear velocity (m/s) 0.5
default_acc Default linear acceleration (m/s²) 0.3

Gripper Config

gripper: digital
gripper_config:
  pin: 3
  close_on_high: true
gripper: hand-e
gripper_config:
  force: 50
  speed: 80

CLI Override Precedence

  1. CLI flagsurkit teach 172.31.1.200 --gripper none
  2. Config file — values from config.yaml
  3. Built-in defaultspoints.db, no gripper, 0.5 m/s velocity

Saving Config

The CLI never modifies your config file automatically. Press Y inside the teach pendant to save. This way you only save settings you've actually tested.

urkit teach 172.31.1.200 --gripper hand-e  # test, then press Y
urkit teach                                 # next time: reads from config

Multiple workcells:

urkit teach --config station_a.yaml   # press Y to save
urkit teach --config station_b.yaml   # separate config

Programmatic

from urkit import load_config, resolve_config

config = load_config()                          # auto-resolve
config = load_config("/path/to/my.yaml")        # explicit path
path = resolve_config()                         # returns Path or None
robot = URRobot.from_config({"robot_ip": "172.31.1.200", "gripper": "2f-85"})

Advanced

Raw RTDE Access

URKit doesn't try to wrap everything. Access the raw ur_rtde interfaces for advanced features:

robot.rtde_control.moveUntilContact([0, 0, -0.02, 0, 0, 0])
robot.rtde_control.forceMode(...)
robot.rtde_control.servoJ(...)
robot.rtde_receive.getActualCurrent()

Full ur_rtde documentation: https://sdurobotics.gitlab.io/ur_rtde/

Connection Lifecycle

robot.connection_lost       # bool: check if RTDE dropped
robot.reconnect_rtde()      # reconnect after a drop
robot.disconnect()          # clean shutdown

Error Handling

from urkit import URKitError, RobotNotInRemoteModeError, RtdeRegisterConflictError

try:
    robot = URRobot(ip="172.31.1.200", points="points.db")
except RobotNotInRemoteModeError:
    print("Enable remote control on the teach pendant!")
except RtdeRegisterConflictError:
    print("Disable EtherNet/IP, PROFINET, or MODBUS!")
except URKitError as e:
    print(f"Error: {e}")

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

urkit-0.2.0.tar.gz (81.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

urkit-0.2.0-py3-none-any.whl (76.2 kB view details)

Uploaded Python 3

File details

Details for the file urkit-0.2.0.tar.gz.

File metadata

  • Download URL: urkit-0.2.0.tar.gz
  • Upload date:
  • Size: 81.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for urkit-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c6b66ad8cfa7fb604f2c0f15a5be0647ea06f8d4455ee35e96f00a2ff405f188
MD5 73afd83adb80a4c41e125d0a9e3f23f3
BLAKE2b-256 8793fe580bb65f2df845f05e774840ef303ac3ce9f6cfaf8c674fc0b8d66d293

See more details on using hashes here.

Provenance

The following attestation bundles were made for urkit-0.2.0.tar.gz:

Publisher: publish.yml on rodolfo-verde/urkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file urkit-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: urkit-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 76.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for urkit-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 38502ff23920a192c88b82767256f6a84d5750ab0f9149e7e8624a8842ee95a0
MD5 40691a30c907fca9aafa7f59690d9a4f
BLAKE2b-256 d755efe99b5b26f836fa6bce6890352afd1c3bea9b19b546df5c2e8cffb4b605

See more details on using hashes here.

Provenance

The following attestation bundles were made for urkit-0.2.0-py3-none-any.whl:

Publisher: publish.yml on rodolfo-verde/urkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page