urkit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. urkit-0.1.0/PKG-INFO +612 -0
  2. urkit-0.1.0/README.md +587 -0
  3. urkit-0.1.0/pyproject.toml +69 -0
  4. urkit-0.1.0/setup.cfg +4 -0
  5. urkit-0.1.0/src/urkit/__init__.py +99 -0
  6. urkit-0.1.0/src/urkit/__main__.py +116 -0
  7. urkit-0.1.0/src/urkit/cli/__init__.py +6 -0
  8. urkit-0.1.0/src/urkit/cli/colors.py +99 -0
  9. urkit-0.1.0/src/urkit/cli/connection_monitor.py +154 -0
  10. urkit-0.1.0/src/urkit/cli/points.py +293 -0
  11. urkit-0.1.0/src/urkit/cli/teach.py +1181 -0
  12. urkit-0.1.0/src/urkit/config.py +78 -0
  13. urkit-0.1.0/src/urkit/connection.py +580 -0
  14. urkit-0.1.0/src/urkit/exceptions.py +51 -0
  15. urkit-0.1.0/src/urkit/geometry.py +413 -0
  16. urkit-0.1.0/src/urkit/gripper/__init__.py +36 -0
  17. urkit-0.1.0/src/urkit/gripper/base.py +116 -0
  18. urkit-0.1.0/src/urkit/gripper/digital.py +140 -0
  19. urkit-0.1.0/src/urkit/gripper/presets.py +113 -0
  20. urkit-0.1.0/src/urkit/gripper/robotiq.py +322 -0
  21. urkit-0.1.0/src/urkit/gripper/robotiq_preamble.py +1356 -0
  22. urkit-0.1.0/src/urkit/io.py +311 -0
  23. urkit-0.1.0/src/urkit/motion.py +504 -0
  24. urkit-0.1.0/src/urkit/points.py +277 -0
  25. urkit-0.1.0/src/urkit/robot.py +1510 -0
  26. urkit-0.1.0/src/urkit/telemetry.py +177 -0
  27. urkit-0.1.0/src/urkit.egg-info/PKG-INFO +612 -0
  28. urkit-0.1.0/src/urkit.egg-info/SOURCES.txt +37 -0
  29. urkit-0.1.0/src/urkit.egg-info/dependency_links.txt +1 -0
  30. urkit-0.1.0/src/urkit.egg-info/entry_points.txt +2 -0
  31. urkit-0.1.0/src/urkit.egg-info/requires.txt +4 -0
  32. urkit-0.1.0/src/urkit.egg-info/top_level.txt +1 -0
  33. urkit-0.1.0/tests/test_exceptions.py +92 -0
  34. urkit-0.1.0/tests/test_geometry.py +187 -0
  35. urkit-0.1.0/tests/test_gripper.py +167 -0
  36. urkit-0.1.0/tests/test_gripper_factory.py +184 -0
  37. urkit-0.1.0/tests/test_gripper_presets.py +152 -0
  38. urkit-0.1.0/tests/test_points.py +356 -0
  39. urkit-0.1.0/tests/test_robot_integration.py +350 -0
urkit-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,612 @@
1
+ Metadata-Version: 2.4
2
+ Name: urkit
3
+ Version: 0.1.0
4
+ Summary: Universal Robots e-Series control toolkit built on ur_rtde
5
+ Author: URKit Contributors
6
+ License: MIT
7
+ Keywords: universal-robots,ur3e,ur5e,ur7e,ur10e,ur12e,ur16e,ur8,ur15,ur18,ur20,ur30,rtde,robotics,industrial-robot
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Topic :: Scientific/Engineering :: Human Machine Interfaces
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: ur_rtde>=1.4.0
22
+ Requires-Dist: pyyaml>=6.0
23
+ Requires-Dist: colorama>=0.4.6
24
+ Requires-Dist: rich>=13.0.0
25
+
26
+ # URKit
27
+
28
+ **URKit** is a Python toolkit for [Universal Robots](https://www.universal-robots.com/) e-Series robots that makes the common stuff simple and gets out of the way for everything else.
29
+
30
+ Built on [`ur_rtde`](https://sdurobotics.gitlab.io/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. It doesn't try to replace `ur_rtde`; it sits on top of it so you can use both in tandem.
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ pip install urkit
36
+ ```
37
+
38
+ Requires Python 3.8+ and a Universal Robots e-Series (UR3e to UR30).
39
+
40
+ ### Robotiq Grippers (optional)
41
+
42
+ If you're using a Robotiq gripper, install the **Robotiq Gripper Control** URCap:
43
+
44
+ 1. Download from [robotiq.com/support](https://robotiq.com/support).
45
+ 2. Copy the `.urcap` file to a USB drive and mount it on the robot.
46
+ 3. `☰` → `Settings` → `System` → `URCaps` → Install from USB.
47
+ 4. Activate and follow the on-screen instructions.
48
+
49
+ ## Robot Setup (one-time)
50
+
51
+ 1. **Network**: `☰` → `System` → `Network`: set the robot's IP and subnet. Make sure your PC is on the same network.
52
+ 2. **Remote Control**: `☰` → `System` → `Remote Control`: Enable. Press the remote/local button on the pendant.
53
+ 3. **Security**: `☰` → `Security` → `Services`: enable RTDE and disable EtherNet/IP, PROFINET, or MODBUS if they're claiming RTDE registers. Save and restart.
54
+
55
+ That's it. No `.urp` files to run, no extra programs needed.
56
+
57
+ ---
58
+
59
+ ## The Workflow
60
+
61
+ The typical workflow with URKit is simple:
62
+
63
+ 1. **Teach points**: Use the interactive CLI to position the robot and save named waypoints.
64
+ 2. **Write code**: Create a robot, move to points by name, apply offsets, run sequences.
65
+ 3. **Iterate**: Add more points in the CLI, tweak your code, repeat.
66
+
67
+ ```python
68
+ from urkit import URRobot, ROBOTIQ_HAND_E
69
+
70
+ # Connect: validates network, remote mode, powers on, sets gripper TCP/payload
71
+ robot = URRobot(ip="172.31.1.200", points="points.db", gripper=ROBOTIQ_HAND_E)
72
+
73
+ # Activate the gripper (required before open/close)
74
+ robot.gripper.activate()
75
+
76
+ # Move to a saved point
77
+ robot.move_to("home")
78
+
79
+ # Apply an offset — 5cm above the pick point
80
+ robot.move_to("pick", offset=[0, 0, 0.05, 0, 0, 0])
81
+ robot.gripper.close()
82
+ robot.move_to("place")
83
+ robot.gripper.open()
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Teaching Points (Interactive CLI)
89
+
90
+ URKit provides two CLI tools: **teach** for interactive robot control, and **points** for browsing saved waypoints.
91
+
92
+ ### Teach Mode
93
+
94
+ The interactive teach pendant for moving the robot, saving points, and checking telemetry:
95
+
96
+ ```bash
97
+ urkit teach 172.31.1.200 # with robot IP
98
+ urkit teach # reads IP from config.yaml
99
+ ```
100
+
101
+ **Flags:**
102
+
103
+ | Flag | Description |
104
+ |------|-------------|
105
+ | `ip` | Robot IP address (positional, overrides config) |
106
+ | `--gripper` | Gripper preset: `2f-85`, `2f-140`, `hand-e`, `digital` |
107
+ | `--gripper-pin` | Digital gripper output pin (default: 0) |
108
+ | `--gripper-force` | Robotiq force 0-100 |
109
+ | `--gripper-speed` | Robotiq speed 0-100 |
110
+ | `--gripper-close-on-high` | Digital polarity: `true` or `false` |
111
+ | `--points` | Path to `points.db` file (overrides config) |
112
+ | `-v`, `--verbose` | Show verbose output (debug connection issues) |
113
+
114
+ ```bash
115
+ urkit teach 172.31.1.200 --gripper hand-e --points /path/to/points.db
116
+ urkit teach --gripper digital --gripper-pin 3
117
+ urkit teach -v # verbose mode
118
+ ```
119
+
120
+ ### Points Explorer
121
+
122
+ Browse saved waypoints with real-time search filtering — no robot connection needed:
123
+
124
+ ```bash
125
+ urkit points # uses default points.db
126
+ urkit points test_points.db # use specific database
127
+ ```
128
+
129
+ **Features:**
130
+ - **Type to search** — Real-time substring filtering
131
+ - **Fuzzy matching** — Type `pk` to find `pick_1` (>60% match)
132
+ - **Smart sorting** — Exact prefix matches first, then substring, then fuzzy
133
+ - **Spatial sorting** — Points ordered by proximity to "home" point (XYZ distance)
134
+ - **Theme-aware** — Automatically adapts colors for light/dark terminals
135
+ - **Arrow keys** — Scroll through results
136
+ - **ESC** — Quit
137
+
138
+ Try `urkit points`, type `pick` to filter, press ESC to exit.
139
+
140
+ ### Key Map
141
+
142
+ All movement and orientation keys support **hold-to-repeat** — hold a key down for continuous motion.
143
+
144
+ <table>
145
+ <tr>
146
+ <th align="center">Movement</th>
147
+ <th align="center">Orientation</th>
148
+ </tr>
149
+ <tr>
150
+ <td align="center" style="width:50%">
151
+ <table>
152
+ <tr><th>Key</th><th>Action</th></tr>
153
+ <tr><td><code>W</code> / <code>S</code></td><td>+X / -X</td></tr>
154
+ <tr><td><code>A</code> / <code>D</code></td><td>+Y / -Y</td></tr>
155
+ <tr><td><code>Q</code> / <code>E</code></td><td>+Z / -Z</td></tr>
156
+ </table>
157
+ </td>
158
+ <td align="center" style="width:50%">
159
+ <table>
160
+ <tr><th>Key</th><th>Action</th></tr>
161
+ <tr><td><code>U</code> / <code>O</code></td><td>Roll - / +</td></tr>
162
+ <tr><td><code>I</code> / <code>K</code></td><td>Pitch - / +</td></tr>
163
+ <tr><td><code>J</code> / <code>L</code></td><td>Yaw - / +</td></tr>
164
+ </table>
165
+ </td>
166
+ </tr>
167
+ </table>
168
+
169
+ <table>
170
+ <tr>
171
+ <th align="center">Step Size</th>
172
+ <th align="center">Gripper</th>
173
+ </tr>
174
+ <tr>
175
+ <td align="center" style="width:50%">
176
+ <table>
177
+ <tr><th>Key</th><th>Action</th></tr>
178
+ <tr><td><code>1</code></td><td>Set linear step (mm)</td></tr>
179
+ <tr><td><code>2</code></td><td>Set angular step (deg)</td></tr>
180
+ <tr><td><code>.</code></td><td>Reset defaults (5 mm / 2°)</td></tr>
181
+ </table>
182
+ </td>
183
+ <td align="center" style="width:50%">
184
+ <table>
185
+ <tr><th>Key</th><th>Action</th></tr>
186
+ <tr><td><code>X</code></td><td>Open</td></tr>
187
+ <tr><td><code>C</code></td><td>Close</td></tr>
188
+ <tr><td><code>V</code></td><td>Set position (mm)</td></tr>
189
+ </table>
190
+ </td>
191
+ </tr>
192
+ </table>
193
+
194
+ <table>
195
+ <tr>
196
+ <th align="center">Points</th>
197
+ <th align="center">Controls</th>
198
+ </tr>
199
+ <tr>
200
+ <td align="center" style="width:50%">
201
+ <table>
202
+ <tr><th>Key</th><th>Action</th></tr>
203
+ <tr><td><code>B</code></td><td><strong>Save</strong> current position</td></tr>
204
+ <tr><td><code>G</code></td><td>Go to saved point</td></tr>
205
+ <tr><td><code>H</code></td><td>Delete saved point</td></tr>
206
+ <tr><td><code>P</code></td><td>Open points explorer</td></tr>
207
+ <tr><td><code>R</code></td><td>Rename saved point</td></tr>
208
+ </table>
209
+ </td>
210
+ <td align="center" style="width:50%">
211
+ <table>
212
+ <tr><th>Key</th><th>Action</th></tr>
213
+ <tr><td><code>F</code></td><td>Freedrive (OFF → ALL → XYZ)</td></tr>
214
+ <tr><td><code>M</code></td><td>Toggle frame (BASE / TOOL)</td></tr>
215
+ <tr><td><code>N</code></td><td>Go To mode (Cartesian / Joint)</td></tr>
216
+ <tr><td><code>T</code></td><td>Orient TCP down (180°)</td></tr>
217
+ <tr><td><code>ESC</code></td><td>Exit</td></tr>
218
+ </table>
219
+ </td>
220
+ </tr>
221
+ </table>
222
+
223
+ Position the robot (keys or freedrive), press **B** to save, and the point is stored in your `points.db`. Load that same file in your code and you're ready to go.
224
+
225
+ ---
226
+
227
+ ## Connecting to the Robot
228
+
229
+ ### Direct
230
+
231
+ ```python
232
+ from urkit import URRobot, ROBOTIQ_HAND_E
233
+
234
+ robot = URRobot(ip="172.31.1.200", points="points.db", gripper=ROBOTIQ_HAND_E)
235
+ ```
236
+
237
+ Set default motion speeds or RTDE frequency:
238
+
239
+ ```python
240
+ robot = URRobot(
241
+ ip="172.31.1.200",
242
+ points="points.db",
243
+ gripper=ROBOTIQ_HAND_E,
244
+ default_vel=0.5, # m/s
245
+ default_acc=0.3, # m/s²
246
+ rtde_frequency=500, # Hz (default: 125)
247
+ )
248
+ ```
249
+
250
+ ### From Config
251
+
252
+ Store your config in a `config.yaml`:
253
+
254
+ ```yaml
255
+ robot_ip: 172.31.1.200
256
+ points_path: points.db
257
+ gripper: hand-e
258
+ default_vel: 0.5
259
+ default_acc: 0.3
260
+ rtde_frequency: 125
261
+ ```
262
+
263
+ Then load it:
264
+
265
+ ```python
266
+ robot = URRobot.from_config("config.yaml")
267
+ robot = URRobot.from_config("config.yaml", ip="10.0.0.50") # override IP
268
+ ```
269
+
270
+ ---
271
+
272
+ ## Gripper Presets
273
+
274
+ Three built-in presets: pick one and it handles mass, CoG, TCP offset, and backend:
275
+
276
+ | Preset | Description |
277
+ |--------|-------------|
278
+ | `ROBOTIQ_HAND_E` | Robotiq 2F-140-E (Hand-E series) |
279
+ | `ROBOTIQ_2F_85` | Robotiq 2F-85 |
280
+ | `ROBOTIQ_2F_140` | Robotiq 2F-140 |
281
+
282
+ Or look up presets programmatically:
283
+
284
+ ```python
285
+ from urkit import PRESETS
286
+
287
+ preset = PRESETS["HAND-E"] # GripperPreset object
288
+ ```
289
+
290
+ Call `activate()` before using the gripper: it resets and calibrates. You decide when:
291
+
292
+ ```python
293
+ robot.gripper.activate() # required before open/close (Robotiq only)
294
+ robot.gripper.is_activated() # check activation state (Robotiq only)
295
+
296
+ robot.gripper.open() # fully open (blocking by default)
297
+ robot.gripper.close() # fully closed, stops on contact
298
+ robot.gripper.open(wait=False) # non-blocking return
299
+ robot.gripper.set_position(20) # 20mm open (Robotiq only, 0 = closed)
300
+ robot.gripper.set_force(50) # grip force: 0-100
301
+ robot.gripper.set_speed(80) # movement speed: 0-100
302
+ robot.gripper.deactivate() # deactivate (Robotiq only)
303
+ ```
304
+
305
+ Override preset values for custom fingers:
306
+
307
+ ```python
308
+ robot = URRobot(ip="172.31.1.200", points="points.db", gripper=ROBOTIQ_HAND_E, max_mm=120)
309
+ ```
310
+
311
+ ### Digital I/O Grippers
312
+
313
+ For suction cups, solenoids, or custom actuators:
314
+
315
+ ```python
316
+ from urkit import URRobot, DigitalGripperConfig
317
+
318
+ # Digital grippers are on/off: no activate(), set_position(), or set_force()
319
+ robot = URRobot(
320
+ ip="172.31.1.200",
321
+ points="points.db",
322
+ gripper=DigitalGripperConfig(pin=3),
323
+ )
324
+
325
+ robot.gripper.open() # turn pin off (release)
326
+ robot.gripper.close() # turn pin on (grab)
327
+ ```
328
+
329
+ ---
330
+
331
+ ## Working with Points
332
+
333
+ Points are managed through the robot object. Save positions with the teach pendant CLI, then reference them by name in your code.
334
+
335
+ The points database is optional — you can create a robot without one and set it later:
336
+
337
+ ```python
338
+ robot = URRobot(ip="172.31.1.200") # no points database
339
+ robot.points_db = "points.db" # attach later
340
+ robot.points_db = None # or remove it
341
+ ```
342
+
343
+ Without a points database, moving to raw poses (`move_to([x, y, z, ...])`) and telemetry still work. Calling `move_to("name")` or `save_point()` will raise `PointError`.
344
+
345
+ ### Moving to Points
346
+
347
+ ```python
348
+ # Linear move (default): straight line in Cartesian space
349
+ robot.move_to("pick")
350
+
351
+ # Joint move: faster, robot picks shortest path in joint space
352
+ robot.move_to("pick", linear=False)
353
+
354
+ # Override velocity (m/s) and acceleration (m/s²) for a single move
355
+ robot.move_to("pick", vel=1.0, acc=0.5)
356
+ ```
357
+
358
+ ### Moving to Raw Poses
359
+
360
+ Skip the database and move to a raw TCP pose:
361
+
362
+ ```python
363
+ # [x, y, z, rx, ry, rz] in meters and radians
364
+ robot.move_to([0.5, 0, 0.3, 0, 0, 0])
365
+ robot.move_to([0.5, 0, 0.3, 0, 0, 0], linear=False)
366
+ ```
367
+
368
+ ### Offsets
369
+
370
+ Apply an offset to any point without creating a new saved point:
371
+
372
+ ```python
373
+ # offset=[dx, dy, dz, droll, dpitch, dyaw]: meters and radians
374
+ robot.move_to("pick", offset=[0, 0, 0.05, 0, 0, 0]) # 5cm above pick
375
+ robot.move_to("pick", offset=[0.02, 0, 0.1, 0, 0, 0]) # 2cm forward, 10cm up
376
+ ```
377
+
378
+ ### Coordinate Frame (BASE / TOOL)
379
+
380
+ Offsets and relative moves use a coordinate frame to determine the direction of movement. The robot has a default frame (BASE by default) that can be changed at any time:
381
+
382
+ ```python
383
+ from urkit import MoveFrame
384
+
385
+ # Set the default frame for all offsets and relative moves
386
+ robot.move_frame = MoveFrame.TOOL
387
+
388
+ # Offset is now relative to the tool's current orientation
389
+ robot.move_to("pick", offset=[0, 0, 0.05]) # 5cm along tool Z
390
+
391
+ # Override per-call
392
+ robot.move_to("pick", offset=[0, 0, 0.05], frame=MoveFrame.BASE)
393
+ ```
394
+
395
+ - **BASE** (default): offset/delta is relative to the robot base. +X always moves along the base X axis.
396
+ - **TOOL**: offset/delta is relative to the TCP orientation. +X moves along the tool's local X axis.
397
+
398
+ ### Point Management
399
+
400
+ ```python
401
+ # Save the current position
402
+ robot.save_point("here")
403
+
404
+ # List all saved points
405
+ names = robot.point_names() # ["home", "pick", "place"]
406
+
407
+ # Rename a point
408
+ robot.rename_point("old", "new")
409
+
410
+ # Delete a point
411
+ robot.delete_point("old")
412
+
413
+ # Export / import points as JSON
414
+ robot.export_points("backup.json")
415
+ robot.import_points("backup.json")
416
+
417
+ # Access the points database directly
418
+ robot.points_db # Points object (read-only)
419
+ robot.points_db = "other.db" # swap to a different database
420
+ ```
421
+
422
+ ### Relative Moves
423
+
424
+ Move relative to the current position:
425
+
426
+ ```python
427
+ # [dx, dy, dz, droll, dpitch, dyaw] in meters and radians
428
+ robot.move_relative([0, 0.01, 0, 0, 0, 0]) # 1cm along Y (base frame)
429
+ robot.move_relative([0, 0, 0.05], frame=MoveFrame.TOOL) # 5cm along tool Z
430
+ ```
431
+
432
+ ### Sequences with Blending
433
+
434
+ Move through multiple waypoints with corner blending:
435
+
436
+ ```python
437
+ # Move through waypoints, stopping at each one
438
+ robot.move_sequence(["a", "b", "c"])
439
+
440
+ # blend_radius rounds corners (in meters): robot doesn't stop at intermediate points
441
+ robot.move_sequence(["a", "b", "c"], blend_radius=0.02)
442
+
443
+ # Joint-space sequence with blending
444
+ robot.move_sequence(["a", "b", "c"], linear=False, blend_radius=0.05)
445
+ ```
446
+
447
+ ---
448
+
449
+ ## Advanced Motion
450
+
451
+ ### Contact Detection
452
+
453
+ Move until force contact is detected (Ctrl+C to stop):
454
+
455
+ ```python
456
+ # Move down at 20mm/s until force changes by 5N (default threshold)
457
+ robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
458
+
459
+ # Higher threshold for heavier contact
460
+ robot.move_until_contact([0, 0, -0.02, 0, 0, 0], threshold=10.0)
461
+ ```
462
+
463
+ ### Velocity Control
464
+
465
+ Move at a constant velocity for a given duration:
466
+
467
+ ```python
468
+ # Move at constant velocity for a given duration (speedL under the hood)
469
+ robot.move_velocity([0, 0, -0.02, 0, 0, 0], duration=1.0) # down at 20mm/s for 1s
470
+ ```
471
+
472
+ ### Freedrive Mode
473
+
474
+ Enable manual robot manipulation:
475
+
476
+ ```python
477
+ from urkit import FreedriveMode
478
+
479
+ # Freedrive lets you manually push the robot: motion commands won't work while active
480
+ robot.enable_freedrive() # all 6 axes free
481
+ robot.enable_freedrive(FreedriveMode.XYZ) # linear axes only
482
+ robot.enable_freedrive(FreedriveMode.ROTATION) # rotation only
483
+ robot.disable_freedrive() # always disable before sending motion commands
484
+
485
+ # Check if freedrive is active
486
+ robot.is_freedrive_active
487
+ ```
488
+
489
+ ### Speed Control
490
+
491
+ ```python
492
+ # Emergency stop: halt all motion immediately
493
+ robot.speed_stop()
494
+
495
+ # Set speed slider (0.0–1.0): hardware-level velocity multiplier
496
+ robot.set_speed_slider(0.5) # all motions run at 50% of their programmed speed
497
+ ```
498
+
499
+ The speed slider is a **hardware-level multiplier** applied by the UR controller itself — it's the same mechanism as the physical slider on the teach pendant.
500
+
501
+ - **Velocity is scaled:** `actual_velocity = vel * slider_factor`
502
+ - **Acceleration is NOT independently scaled** — the raw `acc` value is passed through, but the controller constrains ramp-up to the capped velocity ceiling
503
+ - **Global & persistent** — stays until you change it or the robot faults/resets
504
+ - Affects all motion commands: moveJ, moveL, trajectory, delta moves, velocity control
505
+
506
+ ```python
507
+ # Example: 0.5 slider × 1.0 m/s movej = 0.5 m/s actual speed
508
+ robot.set_speed_slider(0.5)
509
+ robot.movej([-1.0, -1.5, 1.5, -1.0, 1.0, 0.0], vel=1.0, acc=0.5)
510
+ ```
511
+
512
+ ```python
513
+ # Inverse kinematics: pose → joint angles
514
+ joints = robot.inverse_kinematics([0.5, 0, 0.3, 0, 0, 0])
515
+ ```
516
+
517
+ ## Telemetry
518
+
519
+ ```python
520
+ # Read real-time robot state
521
+ pose = robot.get_tcp_pose() # [x, y, z, rx, ry, rz]: meters/radians
522
+ joints = robot.get_joint_positions() # [j0..j5]: radians
523
+ force = robot.get_tcp_force() # [fx, fy, fz, mx, my, mz]: Newtons/Nm
524
+ mode = robot.get_robot_mode() # "REMOTE_CONTROL", "SERVOING", etc.
525
+ scaling = robot.get_speed_scaling() # actual vs programmed speed (0.0-1.0)
526
+ payload = robot.get_payload() # configured payload mass (kg)
527
+ robot.is_protective_stopped() # bool: robot hit something or was pushed
528
+ robot.is_emergency_stopped() # bool: e-stop pressed
529
+
530
+ # Get current pose + joints as a dict
531
+ pos = robot.current_point()
532
+ print(pos["pose"]) # [x, y, z, rx, ry, rz]
533
+ print(pos["joints"]) # [j0, j1, j2, j3, j4, j5]
534
+ ```
535
+
536
+ ---
537
+
538
+ ## Digital I/O
539
+
540
+ ```python
541
+ # Set a single output (pins 0-7 standard, 8-15 configurable)
542
+ robot.set_digital_output(0, True)
543
+
544
+ # Set multiple outputs at once, or clear all
545
+ robot.set_digital_outputs({0: True, 1: False, 8: True})
546
+ robot.set_digital_outputs(False)
547
+
548
+ # Read inputs (pins 0-17, including tool pins 16-17)
549
+ robot.get_digital_input(0)
550
+ robot.get_analog_input(0)
551
+ robot.get_tool_input(0)
552
+
553
+ # Read outputs
554
+ robot.get_digital_output(0)
555
+ robot.get_analog_output(0)
556
+ robot.get_tool_output(0)
557
+
558
+ # Block until a digital input changes (useful for limit switches, sensors)
559
+ if not robot.wait_for_input(0, True, timeout=10.0):
560
+ raise TimeoutError("Limit switch not triggered")
561
+ ```
562
+
563
+ ---
564
+
565
+ ## Advanced: Raw RTDE Access
566
+
567
+ URKit doesn't try to wrap everything. For advanced features like `forceMode`, `servoJ`, `getActualCurrent`, and more, access the raw `ur_rtde` interfaces:
568
+
569
+ ```python
570
+ # rtde_control and rtde_receive give you the full ur_rtde API
571
+ robot.rtde_control.moveUntilContact([0, 0, -0.02, 0, 0, 0])
572
+ robot.rtde_control.forceMode(...)
573
+ robot.rtde_control.servoJ(...)
574
+
575
+ # Read robot current, temperature, or anything ur_rtde exposes
576
+ robot.rtde_receive.getActualCurrent()
577
+ ```
578
+
579
+ Full `ur_rtde` documentation: <https://sdurobotics.gitlab.io/ur_rtde/>
580
+
581
+ ---
582
+
583
+ ## Connection Lifecycle
584
+
585
+ ```python
586
+ # Check if RTDE connection dropped
587
+ robot.connection_lost
588
+
589
+ # Reconnect RTDE after a drop
590
+ robot.reconnect_rtde()
591
+
592
+ # Disconnect and clean up
593
+ robot.disconnect()
594
+ ```
595
+
596
+ ---
597
+
598
+ ## Error Handling
599
+
600
+ ```python
601
+ from urkit import URKitError, RobotNotInRemoteModeError, RtdeRegisterConflictError
602
+
603
+ try:
604
+ robot = URRobot(ip="172.31.1.200", points="points.db")
605
+ except RobotNotInRemoteModeError:
606
+ print("Enable remote control on the teach pendant!")
607
+ except RtdeRegisterConflictError:
608
+ print("Disable EtherNet/IP, PROFINET, or MODBUS!")
609
+ except URKitError as e:
610
+ # Catch-all for any urkit error (connection, motion, gripper, etc.)
611
+ print(f"Error: {e}")
612
+ ```