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.

Example

If you have a Robotiq gripper, install the Robotiq Gripper Control URCap first: download from robotiq.com/support, copy the .urcap to a USB drive, and install via SettingsSystemURCaps.

from urkit import URRobot, ROBOTIQ_2F_85  # or ROBOTIQ_2F_140, ROBOTIQ_HAND_E, or gripper=none

robot = URRobot(ip="172.31.1.200", points="points.db", gripper=ROBOTIQ_2F_85)
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

Resolve a Pose

Get a pose without moving — useful for logging, comparisons, or custom motion:

pose = robot.get_pose("pick")
pose = robot.get_pose("pick", offset=[0, 0, 0.05, 0, 0, 0])  # with offset
robot.move_to(pose)  # move to the resolved pose later

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 Monitoring

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

disconnect() is called automatically when the robot object is garbage collected.

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.1.tar.gz (82.2 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.1-py3-none-any.whl (76.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: urkit-0.2.1.tar.gz
  • Upload date:
  • Size: 82.2 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.1.tar.gz
Algorithm Hash digest
SHA256 27da3103cd0bcc7aa0ad5b9417fe11807718f4be8468b16aeaee528a257b7a04
MD5 111a178a26a4515ac888baa67f6aa129
BLAKE2b-256 e1db4658e21a8f1cffbaa9f895f5a5680a6057c8a11cd7ea871ba7709a7d8c7f

See more details on using hashes here.

Provenance

The following attestation bundles were made for urkit-0.2.1.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.1-py3-none-any.whl.

File metadata

  • Download URL: urkit-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 76.4 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d9e6703d7b6a12dcf95458900a0778b2fb559bbc906bbd77da2ca4000376aff4
MD5 bca1daeb049f8cb9b4f1ecd9cfa7e3a8
BLAKE2b-256 c78754bcba4b49b24662d0d9c197bcbc94bec3432f2d4ddf98e88ab788804fc9

See more details on using hashes here.

Provenance

The following attestation bundles were made for urkit-0.2.1-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