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.
- {urkit-0.3.0 → urkit-3.1}/PKG-INFO +73 -2
- {urkit-0.3.0 → urkit-3.1}/README.md +72 -1
- {urkit-0.3.0 → urkit-3.1}/pyproject.toml +5 -1
- {urkit-0.3.0 → urkit-3.1}/src/urkit/__init__.py +1 -1
- {urkit-0.3.0 → urkit-3.1}/src/urkit/cli/points.py +1 -22
- {urkit-0.3.0 → urkit-3.1}/src/urkit/cli/teach.py +214 -45
- {urkit-0.3.0 → urkit-3.1}/src/urkit/config.py +1 -1
- {urkit-0.3.0 → urkit-3.1}/src/urkit/geometry.py +5 -5
- {urkit-0.3.0 → urkit-3.1}/src/urkit/gripper/base.py +12 -23
- {urkit-0.3.0 → urkit-3.1}/src/urkit/gripper/digital.py +1 -1
- {urkit-0.3.0 → urkit-3.1}/src/urkit/gripper/robotiq.py +6 -3
- {urkit-0.3.0 → urkit-3.1}/src/urkit/motion.py +12 -5
- {urkit-0.3.0 → urkit-3.1}/src/urkit/points.py +3 -2
- {urkit-0.3.0 → urkit-3.1}/src/urkit/robot.py +78 -25
- {urkit-0.3.0 → urkit-3.1}/src/urkit.egg-info/PKG-INFO +73 -2
- {urkit-0.3.0 → urkit-3.1}/setup.cfg +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit/__main__.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit/cli/__init__.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit/cli/colors.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit/cli/connection_monitor.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit/connection.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit/exceptions.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit/gripper/__init__.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit/gripper/presets.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit/gripper/robotiq_preamble.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit/io.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit/telemetry.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit.egg-info/SOURCES.txt +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit.egg-info/dependency_links.txt +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit.egg-info/entry_points.txt +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit.egg-info/requires.txt +0 -0
- {urkit-0.3.0 → urkit-3.1}/src/urkit.egg-info/top_level.txt +0 -0
- {urkit-0.3.0 → urkit-3.1}/tests/test_exceptions.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/tests/test_geometry.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/tests/test_gripper.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/tests/test_gripper_factory.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/tests/test_gripper_presets.py +0 -0
- {urkit-0.3.0 → urkit-3.1}/tests/test_points.py +0 -0
- {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:
|
|
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 = "
|
|
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
|
|
@@ -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
|