urkit 0.3.0__tar.gz → 3.1__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.3.0 → urkit-3.1}/PKG-INFO +73 -2
  2. {urkit-0.3.0 → urkit-3.1}/README.md +72 -1
  3. {urkit-0.3.0 → urkit-3.1}/pyproject.toml +5 -1
  4. {urkit-0.3.0 → urkit-3.1}/src/urkit/__init__.py +1 -1
  5. {urkit-0.3.0 → urkit-3.1}/src/urkit/cli/points.py +1 -22
  6. {urkit-0.3.0 → urkit-3.1}/src/urkit/cli/teach.py +214 -45
  7. {urkit-0.3.0 → urkit-3.1}/src/urkit/config.py +1 -1
  8. {urkit-0.3.0 → urkit-3.1}/src/urkit/geometry.py +5 -5
  9. {urkit-0.3.0 → urkit-3.1}/src/urkit/gripper/base.py +12 -23
  10. {urkit-0.3.0 → urkit-3.1}/src/urkit/gripper/digital.py +1 -1
  11. {urkit-0.3.0 → urkit-3.1}/src/urkit/gripper/robotiq.py +6 -3
  12. {urkit-0.3.0 → urkit-3.1}/src/urkit/motion.py +12 -5
  13. {urkit-0.3.0 → urkit-3.1}/src/urkit/points.py +3 -2
  14. {urkit-0.3.0 → urkit-3.1}/src/urkit/robot.py +78 -25
  15. {urkit-0.3.0 → urkit-3.1}/src/urkit.egg-info/PKG-INFO +73 -2
  16. {urkit-0.3.0 → urkit-3.1}/setup.cfg +0 -0
  17. {urkit-0.3.0 → urkit-3.1}/src/urkit/__main__.py +0 -0
  18. {urkit-0.3.0 → urkit-3.1}/src/urkit/cli/__init__.py +0 -0
  19. {urkit-0.3.0 → urkit-3.1}/src/urkit/cli/colors.py +0 -0
  20. {urkit-0.3.0 → urkit-3.1}/src/urkit/cli/connection_monitor.py +0 -0
  21. {urkit-0.3.0 → urkit-3.1}/src/urkit/connection.py +0 -0
  22. {urkit-0.3.0 → urkit-3.1}/src/urkit/exceptions.py +0 -0
  23. {urkit-0.3.0 → urkit-3.1}/src/urkit/gripper/__init__.py +0 -0
  24. {urkit-0.3.0 → urkit-3.1}/src/urkit/gripper/presets.py +0 -0
  25. {urkit-0.3.0 → urkit-3.1}/src/urkit/gripper/robotiq_preamble.py +0 -0
  26. {urkit-0.3.0 → urkit-3.1}/src/urkit/io.py +0 -0
  27. {urkit-0.3.0 → urkit-3.1}/src/urkit/telemetry.py +0 -0
  28. {urkit-0.3.0 → urkit-3.1}/src/urkit.egg-info/SOURCES.txt +0 -0
  29. {urkit-0.3.0 → urkit-3.1}/src/urkit.egg-info/dependency_links.txt +0 -0
  30. {urkit-0.3.0 → urkit-3.1}/src/urkit.egg-info/entry_points.txt +0 -0
  31. {urkit-0.3.0 → urkit-3.1}/src/urkit.egg-info/requires.txt +0 -0
  32. {urkit-0.3.0 → urkit-3.1}/src/urkit.egg-info/top_level.txt +0 -0
  33. {urkit-0.3.0 → urkit-3.1}/tests/test_exceptions.py +0 -0
  34. {urkit-0.3.0 → urkit-3.1}/tests/test_geometry.py +0 -0
  35. {urkit-0.3.0 → urkit-3.1}/tests/test_gripper.py +0 -0
  36. {urkit-0.3.0 → urkit-3.1}/tests/test_gripper_factory.py +0 -0
  37. {urkit-0.3.0 → urkit-3.1}/tests/test_gripper_presets.py +0 -0
  38. {urkit-0.3.0 → urkit-3.1}/tests/test_points.py +0 -0
  39. {urkit-0.3.0 → urkit-3.1}/tests/test_robot_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: urkit
3
- Version: 0.3.0
3
+ Version: 3.1
4
4
  Summary: Universal Robots e-Series control toolkit built on ur_rtde
5
5
  Author: URKit Contributors
6
6
  License: MIT
@@ -195,7 +195,7 @@ All movement and orientation keys support **hold-to-repeat**.
195
195
  <table>
196
196
  <tr><th>Key</th><th>Action</th></tr>
197
197
  <tr><td><code>B</code></td><td><strong>Save</strong> current position</td></tr>
198
- <tr><td><code>G</code></td><td>Go to saved point</td></tr>
198
+ <tr><td><code>G</code></td><td>Go to saved point (<code>Space</code> to cancel mid-move, <code>ESC</code> to exit)</td></tr>
199
199
  <tr><td><code>H</code></td><td>Delete saved point</td></tr>
200
200
  <tr><td><code>P</code></td><td>Open points explorer</td></tr>
201
201
  <tr><td><code>R</code></td><td>Rename saved point</td></tr>
@@ -215,6 +215,35 @@ All movement and orientation keys support **hold-to-repeat**.
215
215
  </tr>
216
216
  </table>
217
217
 
218
+ ### Joint Display
219
+
220
+ The teach pendant shows live joint angles alongside TCP position and orientation:
221
+
222
+ ```
223
+ Position X=+0.432 Y=+0.111 Z=+0.227
224
+ Orientation R=+131.3 P=-121.0 Y= +8.0
225
+ Joints J1=+150.0 J2=+020.0 J3=+160.0
226
+ J4=+050.0 J5=-080.0 J6=+157.0
227
+ ```
228
+
229
+ Joint angles color-code proximity to mechanical limits:
230
+
231
+ - **Yellow**: within 10% of joint range (warning)
232
+ - **Red**: within 5% of joint range (danger)
233
+
234
+ UR e-Series joint limits:
235
+
236
+ | Joint | Range | Notes |
237
+ |-------|-------|-------|
238
+ | J1 (shoulder pan) | ±360° | Full rotation |
239
+ | J2 (shoulder lift) | ±360° | Full rotation |
240
+ | J3 (elbow) | ±180° | Physically restricted — shoulder lift gets in the way |
241
+ | J4 (wrist 1) | ±360° | Full rotation |
242
+ | J5 (wrist 2) | ±360° | Full rotation |
243
+ | J6 (wrist 3) | ±360° | Tool flange unlimited rotation |
244
+
245
+ Thresholds scale with each joint's range, so warning zones feel proportional across all joints.
246
+
218
247
  ### Safety
219
248
 
220
249
  By default, **Go To** and **TCP Down** movements use a slow velocity (0.125 m/s) so its safer for anyone standing near the robot. The user's speed slider still applies as a global multiplier on top of this.
@@ -325,10 +354,34 @@ robot.points_db = "points.db"
325
354
  robot.move_to("pick") # linear move (default)
326
355
  robot.move_to("pick", linear=False) # joint move
327
356
  robot.move_to("pick", vel=1.0, acc=0.5) # override speed
357
+ robot.move_to("pick", asynchronous=True) # non-blocking, returns immediately
328
358
  ```
329
359
 
330
360
  - **Linear (moveL):** TCP moves in a straight line. Predictable path, slower near complex orientations.
331
361
  - **Joint (moveJ):** Each joint moves simultaneously. Faster, but the TCP follows an arc.
362
+ - **Asynchronous:** When `asynchronous=True`, the move runs in the UR controller's background thread and the method returns immediately.
363
+
364
+ #### Non-Blocking Moves
365
+
366
+ Start a move and do other things while it runs:
367
+
368
+ ```python
369
+ robot.move_to("pick", asynchronous=True)
370
+
371
+ # Poll until done
372
+ while robot.is_moving():
373
+ time.sleep(0.01)
374
+
375
+ # Or cancel mid-move
376
+ robot.move_to("pick", asynchronous=True)
377
+ while robot.is_moving():
378
+ if should_cancel:
379
+ robot.stop() # sends stopL + stopJ
380
+ break
381
+ time.sleep(0.01)
382
+ ```
383
+
384
+ **Teach pendant Go To** uses this pattern internally — Space cancels the move and returns to the menu.
332
385
 
333
386
  #### Pose Format
334
387
 
@@ -420,6 +473,7 @@ robot.is_freedrive_active # check state
420
473
  #### Speed Control
421
474
 
422
475
  ```python
476
+ robot.stop() # stop current move immediately (stopL + stopJ)
423
477
  robot.speed_stop() # stop velocity-controlled motion (not E-stop)
424
478
  robot.set_speed_slider(0.5) # 50% velocity cap
425
479
  robot.get_speed_slider() # read current slider (0.0-1.0)
@@ -441,9 +495,26 @@ joints = robot.get_joint_positions() # [j0..j5]
441
495
  force = robot.get_tcp_force() # [fx, fy, fz, mx, my, mz]
442
496
  mode = robot.get_robot_mode() # "REMOTE_CONTROL", "SERVOING", etc.
443
497
  payload = robot.get_payload() # kg
498
+ robot.is_moving() # bool — any joint/TCP velocity non-zero
444
499
  robot.is_protective_stopped() # bool
445
500
  robot.is_emergency_stopped() # bool
446
501
  robot.current_point() # {"pose": [...], "joints": [...]}
502
+ robot.is_at_pose(target) # bool — TCP within 1mm / 0.5°
503
+ robot.is_at_joints(target) # bool — all joints within 0.001 rad
504
+ ```
505
+
506
+ `is_moving()` is the simplest way to poll — `is_at_pose()` / `is_at_joints()` for precise target checking:
507
+
508
+ ```python
509
+ robot.move_to("pick", asynchronous=True)
510
+
511
+ # Simple: wait until robot stops
512
+ while robot.is_moving():
513
+ time.sleep(0.01)
514
+
515
+ # Precise: check against specific target
516
+ while not robot.is_at_pose(target_pose):
517
+ time.sleep(0.01)
447
518
  ```
448
519
 
449
520
  ### Digital I/O
@@ -169,7 +169,7 @@ All movement and orientation keys support **hold-to-repeat**.
169
169
  <table>
170
170
  <tr><th>Key</th><th>Action</th></tr>
171
171
  <tr><td><code>B</code></td><td><strong>Save</strong> current position</td></tr>
172
- <tr><td><code>G</code></td><td>Go to saved point</td></tr>
172
+ <tr><td><code>G</code></td><td>Go to saved point (<code>Space</code> to cancel mid-move, <code>ESC</code> to exit)</td></tr>
173
173
  <tr><td><code>H</code></td><td>Delete saved point</td></tr>
174
174
  <tr><td><code>P</code></td><td>Open points explorer</td></tr>
175
175
  <tr><td><code>R</code></td><td>Rename saved point</td></tr>
@@ -189,6 +189,35 @@ All movement and orientation keys support **hold-to-repeat**.
189
189
  </tr>
190
190
  </table>
191
191
 
192
+ ### Joint Display
193
+
194
+ The teach pendant shows live joint angles alongside TCP position and orientation:
195
+
196
+ ```
197
+ Position X=+0.432 Y=+0.111 Z=+0.227
198
+ Orientation R=+131.3 P=-121.0 Y= +8.0
199
+ Joints J1=+150.0 J2=+020.0 J3=+160.0
200
+ J4=+050.0 J5=-080.0 J6=+157.0
201
+ ```
202
+
203
+ Joint angles color-code proximity to mechanical limits:
204
+
205
+ - **Yellow**: within 10% of joint range (warning)
206
+ - **Red**: within 5% of joint range (danger)
207
+
208
+ UR e-Series joint limits:
209
+
210
+ | Joint | Range | Notes |
211
+ |-------|-------|-------|
212
+ | J1 (shoulder pan) | ±360° | Full rotation |
213
+ | J2 (shoulder lift) | ±360° | Full rotation |
214
+ | J3 (elbow) | ±180° | Physically restricted — shoulder lift gets in the way |
215
+ | J4 (wrist 1) | ±360° | Full rotation |
216
+ | J5 (wrist 2) | ±360° | Full rotation |
217
+ | J6 (wrist 3) | ±360° | Tool flange unlimited rotation |
218
+
219
+ Thresholds scale with each joint's range, so warning zones feel proportional across all joints.
220
+
192
221
  ### Safety
193
222
 
194
223
  By default, **Go To** and **TCP Down** movements use a slow velocity (0.125 m/s) so its safer for anyone standing near the robot. The user's speed slider still applies as a global multiplier on top of this.
@@ -299,10 +328,34 @@ robot.points_db = "points.db"
299
328
  robot.move_to("pick") # linear move (default)
300
329
  robot.move_to("pick", linear=False) # joint move
301
330
  robot.move_to("pick", vel=1.0, acc=0.5) # override speed
331
+ robot.move_to("pick", asynchronous=True) # non-blocking, returns immediately
302
332
  ```
303
333
 
304
334
  - **Linear (moveL):** TCP moves in a straight line. Predictable path, slower near complex orientations.
305
335
  - **Joint (moveJ):** Each joint moves simultaneously. Faster, but the TCP follows an arc.
336
+ - **Asynchronous:** When `asynchronous=True`, the move runs in the UR controller's background thread and the method returns immediately.
337
+
338
+ #### Non-Blocking Moves
339
+
340
+ Start a move and do other things while it runs:
341
+
342
+ ```python
343
+ robot.move_to("pick", asynchronous=True)
344
+
345
+ # Poll until done
346
+ while robot.is_moving():
347
+ time.sleep(0.01)
348
+
349
+ # Or cancel mid-move
350
+ robot.move_to("pick", asynchronous=True)
351
+ while robot.is_moving():
352
+ if should_cancel:
353
+ robot.stop() # sends stopL + stopJ
354
+ break
355
+ time.sleep(0.01)
356
+ ```
357
+
358
+ **Teach pendant Go To** uses this pattern internally — Space cancels the move and returns to the menu.
306
359
 
307
360
  #### Pose Format
308
361
 
@@ -394,6 +447,7 @@ robot.is_freedrive_active # check state
394
447
  #### Speed Control
395
448
 
396
449
  ```python
450
+ robot.stop() # stop current move immediately (stopL + stopJ)
397
451
  robot.speed_stop() # stop velocity-controlled motion (not E-stop)
398
452
  robot.set_speed_slider(0.5) # 50% velocity cap
399
453
  robot.get_speed_slider() # read current slider (0.0-1.0)
@@ -415,9 +469,26 @@ joints = robot.get_joint_positions() # [j0..j5]
415
469
  force = robot.get_tcp_force() # [fx, fy, fz, mx, my, mz]
416
470
  mode = robot.get_robot_mode() # "REMOTE_CONTROL", "SERVOING", etc.
417
471
  payload = robot.get_payload() # kg
472
+ robot.is_moving() # bool — any joint/TCP velocity non-zero
418
473
  robot.is_protective_stopped() # bool
419
474
  robot.is_emergency_stopped() # bool
420
475
  robot.current_point() # {"pose": [...], "joints": [...]}
476
+ robot.is_at_pose(target) # bool — TCP within 1mm / 0.5°
477
+ robot.is_at_joints(target) # bool — all joints within 0.001 rad
478
+ ```
479
+
480
+ `is_moving()` is the simplest way to poll — `is_at_pose()` / `is_at_joints()` for precise target checking:
481
+
482
+ ```python
483
+ robot.move_to("pick", asynchronous=True)
484
+
485
+ # Simple: wait until robot stops
486
+ while robot.is_moving():
487
+ time.sleep(0.01)
488
+
489
+ # Precise: check against specific target
490
+ while not robot.is_at_pose(target_pose):
491
+ time.sleep(0.01)
421
492
  ```
422
493
 
423
494
  ### Digital I/O
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "urkit"
7
- version = "0.3.0"
7
+ version = "3.1"
8
8
  description = "Universal Robots e-Series control toolkit built on ur_rtde"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -71,3 +71,7 @@ line-length = 99
71
71
 
72
72
  [tool.ruff.lint]
73
73
  select = ["E", "F", "I", "N", "W", "UP"]
74
+
75
+ [tool.mypy]
76
+ follow_imports = "skip"
77
+ ignore_missing_imports = true
@@ -24,7 +24,7 @@ Quick start::
24
24
 
25
25
  from __future__ import annotations
26
26
 
27
- __version__ = "0.3.0"
27
+ __version__ = "3.1"
28
28
 
29
29
  from urkit.config import load_config, resolve_config
30
30
  from urkit.exceptions import (
@@ -73,7 +73,7 @@ def _explore_points(args) -> None:
73
73
  # Resolve points path
74
74
  config = load_config()
75
75
  points_path = args.points_path or config.get("points_path") or "points.db"
76
- points_path = Path(points_path).resolve()
76
+ points_path = Path(points_path).resolve() # type: ignore
77
77
 
78
78
  if not points_path.exists():
79
79
  print(f"Error: Points database not found: {points_path}")
@@ -126,8 +126,6 @@ def _interactive_points_filter(points_db: Points, all_points: list[str], points_
126
126
  termios.tcsetattr(sys.stdin, termios.TCSADRAIN, new_settings)
127
127
 
128
128
  fd = sys.stdin.fileno()
129
- # Rich auto-detects dark/light terminal theme
130
- console = Console(force_terminal=True)
131
129
 
132
130
  try:
133
131
  while True:
@@ -276,25 +274,6 @@ def _interactive_points_filter(points_db: Points, all_points: list[str], points_
276
274
  filter_str = filter_str[:-1]
277
275
  scroll = 0
278
276
  needs_redraw = True
279
- elif ch == "\r": # Enter — reload points from database
280
- if not points_path.exists():
281
- refresh_error = "Database file deleted"
282
- all_points = []
283
- all_points_sorted = []
284
- filtered = []
285
- scroll = 0
286
- needs_redraw = True
287
- else:
288
- try:
289
- all_points = points_db.list()
290
- refresh_error = None
291
- all_points_sorted = sorted(all_points)
292
- filtered = [p for p in all_points_sorted if filter_str == "" or filter_str.lower() in p.lower()]
293
- scroll = 0
294
- needs_redraw = True
295
- except Exception as e:
296
- refresh_error = str(e)
297
- needs_redraw = True
298
277
  elif ch.isprintable():
299
278
  filter_str += ch
300
279
  scroll = 0