urkit 0.3.0__tar.gz → 0.3.2__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-0.3.2}/PKG-INFO +94 -2
  2. {urkit-0.3.0 → urkit-0.3.2}/README.md +93 -1
  3. {urkit-0.3.0 → urkit-0.3.2}/pyproject.toml +5 -1
  4. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/__init__.py +1 -1
  5. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/cli/points.py +1 -22
  6. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/cli/teach.py +252 -48
  7. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/config.py +1 -1
  8. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/geometry.py +5 -5
  9. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/gripper/base.py +12 -23
  10. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/gripper/digital.py +1 -1
  11. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/gripper/robotiq.py +9 -3
  12. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/motion.py +12 -5
  13. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/points.py +3 -2
  14. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/robot.py +78 -25
  15. {urkit-0.3.0 → urkit-0.3.2}/src/urkit.egg-info/PKG-INFO +94 -2
  16. {urkit-0.3.0 → urkit-0.3.2}/setup.cfg +0 -0
  17. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/__main__.py +0 -0
  18. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/cli/__init__.py +0 -0
  19. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/cli/colors.py +0 -0
  20. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/cli/connection_monitor.py +0 -0
  21. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/connection.py +0 -0
  22. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/exceptions.py +0 -0
  23. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/gripper/__init__.py +0 -0
  24. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/gripper/presets.py +0 -0
  25. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/gripper/robotiq_preamble.py +0 -0
  26. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/io.py +0 -0
  27. {urkit-0.3.0 → urkit-0.3.2}/src/urkit/telemetry.py +0 -0
  28. {urkit-0.3.0 → urkit-0.3.2}/src/urkit.egg-info/SOURCES.txt +0 -0
  29. {urkit-0.3.0 → urkit-0.3.2}/src/urkit.egg-info/dependency_links.txt +0 -0
  30. {urkit-0.3.0 → urkit-0.3.2}/src/urkit.egg-info/entry_points.txt +0 -0
  31. {urkit-0.3.0 → urkit-0.3.2}/src/urkit.egg-info/requires.txt +0 -0
  32. {urkit-0.3.0 → urkit-0.3.2}/src/urkit.egg-info/top_level.txt +0 -0
  33. {urkit-0.3.0 → urkit-0.3.2}/tests/test_exceptions.py +0 -0
  34. {urkit-0.3.0 → urkit-0.3.2}/tests/test_geometry.py +0 -0
  35. {urkit-0.3.0 → urkit-0.3.2}/tests/test_gripper.py +0 -0
  36. {urkit-0.3.0 → urkit-0.3.2}/tests/test_gripper_factory.py +0 -0
  37. {urkit-0.3.0 → urkit-0.3.2}/tests/test_gripper_presets.py +0 -0
  38. {urkit-0.3.0 → urkit-0.3.2}/tests/test_points.py +0 -0
  39. {urkit-0.3.0 → urkit-0.3.2}/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: 0.3.2
4
4
  Summary: Universal Robots e-Series control toolkit built on ur_rtde
5
5
  Author: URKit Contributors
6
6
  License: MIT
@@ -52,6 +52,7 @@ Built on [`ur_rtde`](https://sdurobotics.gitlab.io/ur_rtde/), it packages the op
52
52
  - [Raw RTDE Access](#raw-rtde-access)
53
53
  - [Connection Lifecycle](#connection-lifecycle)
54
54
  - [Error Handling](#error-handling)
55
+ - [Changelog](#changelog)
55
56
 
56
57
  ---
57
58
 
@@ -189,13 +190,16 @@ All movement and orientation keys support **hold-to-repeat**.
189
190
  <tr><td><code>X</code></td><td>Open</td></tr>
190
191
  <tr><td><code>C</code></td><td>Close</td></tr>
191
192
  <tr><td><code>V</code></td><td>Set position (mm)</td></tr>
193
+ <tr><td><code>6</code></td><td>Set speed (0-100)</td></tr>
194
+ <tr><td><code>7</code></td><td>Set force (0-100)</td></tr>
195
+ <tr><td colspan="3">Gripper line shows: `Connected 50.0mm (0%) F=100 S=100`</td></tr>
192
196
  </table>
193
197
  </td>
194
198
  <td align="center" style="width:33%">
195
199
  <table>
196
200
  <tr><th>Key</th><th>Action</th></tr>
197
201
  <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>
202
+ <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
203
  <tr><td><code>H</code></td><td>Delete saved point</td></tr>
200
204
  <tr><td><code>P</code></td><td>Open points explorer</td></tr>
201
205
  <tr><td><code>R</code></td><td>Rename saved point</td></tr>
@@ -215,6 +219,35 @@ All movement and orientation keys support **hold-to-repeat**.
215
219
  </tr>
216
220
  </table>
217
221
 
222
+ ### Joint Display
223
+
224
+ The teach pendant shows live joint angles alongside TCP position and orientation:
225
+
226
+ ```
227
+ Position X=+0.432 Y=+0.111 Z=+0.227
228
+ Orientation R=+131.3 P=-121.0 Y= +8.0
229
+ Joints J1=+150.0 J2=+020.0 J3=+160.0
230
+ J4=+050.0 J5=-080.0 J6=+157.0
231
+ ```
232
+
233
+ Joint angles color-code proximity to mechanical limits:
234
+
235
+ - **Yellow**: within 10% of joint range (warning)
236
+ - **Red**: within 5% of joint range (danger)
237
+
238
+ UR e-Series joint limits:
239
+
240
+ | Joint | Range | Notes |
241
+ |-------|-------|-------|
242
+ | J1 (shoulder pan) | ±360° | Full rotation |
243
+ | J2 (shoulder lift) | ±360° | Full rotation |
244
+ | J3 (elbow) | ±180° | Physically restricted — shoulder lift gets in the way |
245
+ | J4 (wrist 1) | ±360° | Full rotation |
246
+ | J5 (wrist 2) | ±360° | Full rotation |
247
+ | J6 (wrist 3) | ±360° | Tool flange unlimited rotation |
248
+
249
+ Thresholds scale with each joint's range, so warning zones feel proportional across all joints.
250
+
218
251
  ### Safety
219
252
 
220
253
  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 +358,34 @@ robot.points_db = "points.db"
325
358
  robot.move_to("pick") # linear move (default)
326
359
  robot.move_to("pick", linear=False) # joint move
327
360
  robot.move_to("pick", vel=1.0, acc=0.5) # override speed
361
+ robot.move_to("pick", asynchronous=True) # non-blocking, returns immediately
328
362
  ```
329
363
 
330
364
  - **Linear (moveL):** TCP moves in a straight line. Predictable path, slower near complex orientations.
331
365
  - **Joint (moveJ):** Each joint moves simultaneously. Faster, but the TCP follows an arc.
366
+ - **Asynchronous:** When `asynchronous=True`, the move runs in the UR controller's background thread and the method returns immediately.
367
+
368
+ #### Non-Blocking Moves
369
+
370
+ Start a move and do other things while it runs:
371
+
372
+ ```python
373
+ robot.move_to("pick", asynchronous=True)
374
+
375
+ # Poll until done
376
+ while robot.is_moving():
377
+ time.sleep(0.01)
378
+
379
+ # Or cancel mid-move
380
+ robot.move_to("pick", asynchronous=True)
381
+ while robot.is_moving():
382
+ if should_cancel:
383
+ robot.stop() # sends stopL + stopJ
384
+ break
385
+ time.sleep(0.01)
386
+ ```
387
+
388
+ **Teach pendant Go To** uses this pattern internally — Space cancels the move and returns to the menu.
332
389
 
333
390
  #### Pose Format
334
391
 
@@ -420,6 +477,7 @@ robot.is_freedrive_active # check state
420
477
  #### Speed Control
421
478
 
422
479
  ```python
480
+ robot.stop() # stop current move immediately (stopL + stopJ)
423
481
  robot.speed_stop() # stop velocity-controlled motion (not E-stop)
424
482
  robot.set_speed_slider(0.5) # 50% velocity cap
425
483
  robot.get_speed_slider() # read current slider (0.0-1.0)
@@ -441,9 +499,26 @@ joints = robot.get_joint_positions() # [j0..j5]
441
499
  force = robot.get_tcp_force() # [fx, fy, fz, mx, my, mz]
442
500
  mode = robot.get_robot_mode() # "REMOTE_CONTROL", "SERVOING", etc.
443
501
  payload = robot.get_payload() # kg
502
+ robot.is_moving() # bool — any joint/TCP velocity non-zero
444
503
  robot.is_protective_stopped() # bool
445
504
  robot.is_emergency_stopped() # bool
446
505
  robot.current_point() # {"pose": [...], "joints": [...]}
506
+ robot.is_at_pose(target) # bool — TCP within 1mm / 0.5°
507
+ robot.is_at_joints(target) # bool — all joints within 0.001 rad
508
+ ```
509
+
510
+ `is_moving()` is the simplest way to poll — `is_at_pose()` / `is_at_joints()` for precise target checking:
511
+
512
+ ```python
513
+ robot.move_to("pick", asynchronous=True)
514
+
515
+ # Simple: wait until robot stops
516
+ while robot.is_moving():
517
+ time.sleep(0.01)
518
+
519
+ # Precise: check against specific target
520
+ while not robot.is_at_pose(target_pose):
521
+ time.sleep(0.01)
447
522
  ```
448
523
 
449
524
  ### Digital I/O
@@ -588,3 +663,20 @@ When the robot enters protective stop or the RTDE connection drops, motion comma
588
663
  ### Connection Notes
589
664
 
590
665
  The `URRobot` constructor takes a few seconds on first call: it validates the connection, checks remote mode, powers on the robot, releases brakes, and connects RTDE. Subsequent calls are faster if the robot is already running.
666
+
667
+ ---
668
+
669
+ ## Changelog
670
+
671
+ See [CHANGELOG.md](CHANGELOG.md) for the full version history.
672
+
673
+ ### Recent Changes
674
+
675
+ - **v3.2** — Gripper speed/force keys (6/7), force/speed display
676
+ - **v3.1** — Async motion, Space cancel, progress bar, joint display
677
+ - **v0.3.0** — Safe/expert mode, speed_stop on exit, safety features
678
+ - **v0.2.1** — `get_pose()` method
679
+ - **v0.2.0** — README restructure
680
+ - **v0.1.1** — `--gripper none`, `--config` flag, save config on demand
681
+ - **v0.1.0** — Initial release
682
+
@@ -26,6 +26,7 @@ Built on [`ur_rtde`](https://sdurobotics.gitlab.io/ur_rtde/), it packages the op
26
26
  - [Raw RTDE Access](#raw-rtde-access)
27
27
  - [Connection Lifecycle](#connection-lifecycle)
28
28
  - [Error Handling](#error-handling)
29
+ - [Changelog](#changelog)
29
30
 
30
31
  ---
31
32
 
@@ -163,13 +164,16 @@ All movement and orientation keys support **hold-to-repeat**.
163
164
  <tr><td><code>X</code></td><td>Open</td></tr>
164
165
  <tr><td><code>C</code></td><td>Close</td></tr>
165
166
  <tr><td><code>V</code></td><td>Set position (mm)</td></tr>
167
+ <tr><td><code>6</code></td><td>Set speed (0-100)</td></tr>
168
+ <tr><td><code>7</code></td><td>Set force (0-100)</td></tr>
169
+ <tr><td colspan="3">Gripper line shows: `Connected 50.0mm (0%) F=100 S=100`</td></tr>
166
170
  </table>
167
171
  </td>
168
172
  <td align="center" style="width:33%">
169
173
  <table>
170
174
  <tr><th>Key</th><th>Action</th></tr>
171
175
  <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>
176
+ <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
177
  <tr><td><code>H</code></td><td>Delete saved point</td></tr>
174
178
  <tr><td><code>P</code></td><td>Open points explorer</td></tr>
175
179
  <tr><td><code>R</code></td><td>Rename saved point</td></tr>
@@ -189,6 +193,35 @@ All movement and orientation keys support **hold-to-repeat**.
189
193
  </tr>
190
194
  </table>
191
195
 
196
+ ### Joint Display
197
+
198
+ The teach pendant shows live joint angles alongside TCP position and orientation:
199
+
200
+ ```
201
+ Position X=+0.432 Y=+0.111 Z=+0.227
202
+ Orientation R=+131.3 P=-121.0 Y= +8.0
203
+ Joints J1=+150.0 J2=+020.0 J3=+160.0
204
+ J4=+050.0 J5=-080.0 J6=+157.0
205
+ ```
206
+
207
+ Joint angles color-code proximity to mechanical limits:
208
+
209
+ - **Yellow**: within 10% of joint range (warning)
210
+ - **Red**: within 5% of joint range (danger)
211
+
212
+ UR e-Series joint limits:
213
+
214
+ | Joint | Range | Notes |
215
+ |-------|-------|-------|
216
+ | J1 (shoulder pan) | ±360° | Full rotation |
217
+ | J2 (shoulder lift) | ±360° | Full rotation |
218
+ | J3 (elbow) | ±180° | Physically restricted — shoulder lift gets in the way |
219
+ | J4 (wrist 1) | ±360° | Full rotation |
220
+ | J5 (wrist 2) | ±360° | Full rotation |
221
+ | J6 (wrist 3) | ±360° | Tool flange unlimited rotation |
222
+
223
+ Thresholds scale with each joint's range, so warning zones feel proportional across all joints.
224
+
192
225
  ### Safety
193
226
 
194
227
  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 +332,34 @@ robot.points_db = "points.db"
299
332
  robot.move_to("pick") # linear move (default)
300
333
  robot.move_to("pick", linear=False) # joint move
301
334
  robot.move_to("pick", vel=1.0, acc=0.5) # override speed
335
+ robot.move_to("pick", asynchronous=True) # non-blocking, returns immediately
302
336
  ```
303
337
 
304
338
  - **Linear (moveL):** TCP moves in a straight line. Predictable path, slower near complex orientations.
305
339
  - **Joint (moveJ):** Each joint moves simultaneously. Faster, but the TCP follows an arc.
340
+ - **Asynchronous:** When `asynchronous=True`, the move runs in the UR controller's background thread and the method returns immediately.
341
+
342
+ #### Non-Blocking Moves
343
+
344
+ Start a move and do other things while it runs:
345
+
346
+ ```python
347
+ robot.move_to("pick", asynchronous=True)
348
+
349
+ # Poll until done
350
+ while robot.is_moving():
351
+ time.sleep(0.01)
352
+
353
+ # Or cancel mid-move
354
+ robot.move_to("pick", asynchronous=True)
355
+ while robot.is_moving():
356
+ if should_cancel:
357
+ robot.stop() # sends stopL + stopJ
358
+ break
359
+ time.sleep(0.01)
360
+ ```
361
+
362
+ **Teach pendant Go To** uses this pattern internally — Space cancels the move and returns to the menu.
306
363
 
307
364
  #### Pose Format
308
365
 
@@ -394,6 +451,7 @@ robot.is_freedrive_active # check state
394
451
  #### Speed Control
395
452
 
396
453
  ```python
454
+ robot.stop() # stop current move immediately (stopL + stopJ)
397
455
  robot.speed_stop() # stop velocity-controlled motion (not E-stop)
398
456
  robot.set_speed_slider(0.5) # 50% velocity cap
399
457
  robot.get_speed_slider() # read current slider (0.0-1.0)
@@ -415,9 +473,26 @@ joints = robot.get_joint_positions() # [j0..j5]
415
473
  force = robot.get_tcp_force() # [fx, fy, fz, mx, my, mz]
416
474
  mode = robot.get_robot_mode() # "REMOTE_CONTROL", "SERVOING", etc.
417
475
  payload = robot.get_payload() # kg
476
+ robot.is_moving() # bool — any joint/TCP velocity non-zero
418
477
  robot.is_protective_stopped() # bool
419
478
  robot.is_emergency_stopped() # bool
420
479
  robot.current_point() # {"pose": [...], "joints": [...]}
480
+ robot.is_at_pose(target) # bool — TCP within 1mm / 0.5°
481
+ robot.is_at_joints(target) # bool — all joints within 0.001 rad
482
+ ```
483
+
484
+ `is_moving()` is the simplest way to poll — `is_at_pose()` / `is_at_joints()` for precise target checking:
485
+
486
+ ```python
487
+ robot.move_to("pick", asynchronous=True)
488
+
489
+ # Simple: wait until robot stops
490
+ while robot.is_moving():
491
+ time.sleep(0.01)
492
+
493
+ # Precise: check against specific target
494
+ while not robot.is_at_pose(target_pose):
495
+ time.sleep(0.01)
421
496
  ```
422
497
 
423
498
  ### Digital I/O
@@ -562,3 +637,20 @@ When the robot enters protective stop or the RTDE connection drops, motion comma
562
637
  ### Connection Notes
563
638
 
564
639
  The `URRobot` constructor takes a few seconds on first call: it validates the connection, checks remote mode, powers on the robot, releases brakes, and connects RTDE. Subsequent calls are faster if the robot is already running.
640
+
641
+ ---
642
+
643
+ ## Changelog
644
+
645
+ See [CHANGELOG.md](CHANGELOG.md) for the full version history.
646
+
647
+ ### Recent Changes
648
+
649
+ - **v3.2** — Gripper speed/force keys (6/7), force/speed display
650
+ - **v3.1** — Async motion, Space cancel, progress bar, joint display
651
+ - **v0.3.0** — Safe/expert mode, speed_stop on exit, safety features
652
+ - **v0.2.1** — `get_pose()` method
653
+ - **v0.2.0** — README restructure
654
+ - **v0.1.1** — `--gripper none`, `--config` flag, save config on demand
655
+ - **v0.1.0** — Initial release
656
+
@@ -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 = "0.3.2"
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__ = "0.3.2"
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