urkit 0.3.25__tar.gz → 0.4.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.
- {urkit-0.3.25 → urkit-0.4.0}/PKG-INFO +295 -140
- {urkit-0.3.25 → urkit-0.4.0}/README.md +294 -139
- {urkit-0.3.25 → urkit-0.4.0}/pyproject.toml +1 -1
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/__init__.py +3 -5
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/cli/points.py +1 -1
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/cli/teach.py +150 -38
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/config.py +7 -12
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/motion.py +34 -14
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/robot.py +234 -294
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/telemetry.py +0 -16
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit.egg-info/PKG-INFO +295 -140
- {urkit-0.3.25 → urkit-0.4.0}/tests/test_gripper_presets.py +1 -1
- {urkit-0.3.25 → urkit-0.4.0}/tests/test_move_sequence.py +49 -38
- {urkit-0.3.25 → urkit-0.4.0}/tests/test_robot_integration.py +17 -23
- {urkit-0.3.25 → urkit-0.4.0}/setup.cfg +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/__main__.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/cli/__init__.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/cli/colors.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/cli/connection_monitor.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/connection.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/exceptions.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/geometry.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/gripper/__init__.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/gripper/base.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/gripper/digital.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/gripper/presets.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/gripper/robotiq.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/gripper/robotiq_preamble.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/io.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit/points.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit.egg-info/SOURCES.txt +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit.egg-info/dependency_links.txt +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit.egg-info/entry_points.txt +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit.egg-info/requires.txt +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/src/urkit.egg-info/top_level.txt +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/tests/test_exceptions.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/tests/test_geometry.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/tests/test_gripper.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/tests/test_gripper_factory.py +0 -0
- {urkit-0.3.25 → urkit-0.4.0}/tests/test_points.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: urkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Universal Robots e-Series control toolkit built on ur_rtde
|
|
5
5
|
Author: URKit Contributors
|
|
6
6
|
License: MIT
|
|
@@ -28,15 +28,39 @@ Requires-Dist: rich>=15
|
|
|
28
28
|
|
|
29
29
|
[](https://pypi.org/project/urkit/)
|
|
30
30
|
|
|
31
|
-
**URKit**
|
|
31
|
+
**URKit** makes it easy to get a Universal Robots e-Series robot moving from Python.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
## What it is
|
|
34
|
+
|
|
35
|
+
A thin layer over [`ur_rtde`](https://sdurobotics.gitlab.io/ur_rtde/) that handles the common stuff: connecting, teaching named points, moving between them, gripper control, telemetry. Raw RTDE interfaces are exposed for anything deeper.
|
|
36
|
+
|
|
37
|
+
No .urp files, no Dashboard API, no extra programs to run on the robot. Just connect over the network and go. Handles power-on, brake release, and RTDE setup in the constructor so you don't have to.
|
|
38
|
+
|
|
39
|
+
Comes with an interactive teach pendant CLI for positioning the robot and saving waypoints, plus a Python API for scripting motion, gripper control, I/O, and telemetry.
|
|
40
|
+
|
|
41
|
+
## When to use this
|
|
42
|
+
|
|
43
|
+
Projects where the robot is part of something bigger. Computer vision, machine learning, sensor fusion, data logging. If your project lives in Python, keep the robot control in Python too.
|
|
44
|
+
|
|
45
|
+
Built for labs and research setups where you need to get the robot moving fast and integrate it with other software. Not designed as a drop-in replacement for Polyscope in production cells, but perfectly capable for anything that runs from a PC.
|
|
46
|
+
|
|
47
|
+
## How it works
|
|
48
|
+
|
|
49
|
+
Use the CLI to position the robot and save named waypoints. Then reference them by name in your code: move to points, apply offsets, run sequences. Points are stored in a local SQLite database, no robot-side setup needed.
|
|
50
|
+
|
|
51
|
+
The typical workflow: teach points with the pendant, write a few lines of Python to string them together, run it. Add vision, add sensors, add logic. The robot is just one component in your pipeline.
|
|
34
52
|
|
|
35
53
|
---
|
|
36
54
|
|
|
37
55
|
## Table of Contents
|
|
38
56
|
|
|
39
57
|
- [Quick Start](#quick-start)
|
|
58
|
+
- [Configuration](#configuration)
|
|
59
|
+
- [Location](#location)
|
|
60
|
+
- [Keys](#keys)
|
|
61
|
+
- [Gripper Config](#gripper-config)
|
|
62
|
+
- [Saving Config](#saving-config)
|
|
63
|
+
- [Programmatic](#programmatic)
|
|
40
64
|
- [Interactive CLI](#interactive-cli)
|
|
41
65
|
- [Teach Mode](#teach-mode)
|
|
42
66
|
- [Points Explorer](#points-explorer)
|
|
@@ -46,8 +70,10 @@ Built on [`ur_rtde`](https://sdurobotics.gitlab.io/ur_rtde/), it packages the op
|
|
|
46
70
|
- [Grippers](#grippers)
|
|
47
71
|
- [Points & Motion](#points--motion)
|
|
48
72
|
- [Telemetry](#telemetry)
|
|
73
|
+
- [More API](#more-api)
|
|
49
74
|
- [Digital I/O](#digital-io)
|
|
50
|
-
- [
|
|
75
|
+
- [Geometry](#geometry)
|
|
76
|
+
- [Robot Lifecycle](#robot-lifecycle)
|
|
51
77
|
- [Advanced](#advanced)
|
|
52
78
|
- [Raw RTDE Access](#raw-rtde-access)
|
|
53
79
|
- [Connection Lifecycle](#connection-lifecycle)
|
|
@@ -62,7 +88,7 @@ Built on [`ur_rtde`](https://sdurobotics.gitlab.io/ur_rtde/), it packages the op
|
|
|
62
88
|
pip install -U urkit
|
|
63
89
|
```
|
|
64
90
|
|
|
65
|
-
The `-U` (upgrade) flag ensures you always get the latest version
|
|
91
|
+
The `-U` (upgrade) flag ensures you always get the latest version. This project is in early development and changes frequently.
|
|
66
92
|
|
|
67
93
|
Requires Python 3.8+ and a Universal Robots e-Series (UR3e to UR30).
|
|
68
94
|
|
|
@@ -102,6 +128,100 @@ The typical workflow:
|
|
|
102
128
|
|
|
103
129
|
---
|
|
104
130
|
|
|
131
|
+
## Configuration
|
|
132
|
+
|
|
133
|
+
URKit uses a YAML config file (`config.yaml`) to persist settings between sessions.
|
|
134
|
+
|
|
135
|
+
### Location
|
|
136
|
+
|
|
137
|
+
URKit searches for `config.yaml` in the current working directory, or an explicit path via `--config`.
|
|
138
|
+
|
|
139
|
+
### Keys
|
|
140
|
+
|
|
141
|
+
| Key | Description | Example |
|
|
142
|
+
|-----|-------------|---------|
|
|
143
|
+
| `robot_ip` | Robot IP address | `192.168.1.50` |
|
|
144
|
+
| `points_path` | Path to SQLite points database | `points.db` |
|
|
145
|
+
| `gripper` | Gripper preset name | `hand-e`, `2f-85`, `2f-140`, `digital` |
|
|
146
|
+
| `ik_reference` | IK reference posture (prevents elbow flipping) | `home` |
|
|
147
|
+
| `default_vel` | Default linear velocity (m/s) | `0.5` |
|
|
148
|
+
| `default_acc` | Default linear acceleration (m/s²) | `0.3` |
|
|
149
|
+
| `expert_mode` | Disable safety speed clamping | `false` |
|
|
150
|
+
|
|
151
|
+
### Gripper Config
|
|
152
|
+
|
|
153
|
+
Built-in preset with overrides:
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
156
|
+
gripper: hand-e
|
|
157
|
+
gripper_config:
|
|
158
|
+
force: 50
|
|
159
|
+
speed: 80
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Digital I/O gripper:
|
|
163
|
+
|
|
164
|
+
```yaml
|
|
165
|
+
gripper: digital
|
|
166
|
+
gripper_config:
|
|
167
|
+
pin: 3
|
|
168
|
+
close_on_high: true
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Custom gripper (arbitrary payload + TCP offset, no backend):
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
gripper:
|
|
175
|
+
mass: 0.5
|
|
176
|
+
center_of_gravity: [0.0, 0.0, 0.0]
|
|
177
|
+
tcp_offset: [0.0, 0.0, 0.175, 0.0, 0.0, 0.0]
|
|
178
|
+
backend: none
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Override physical properties on a built-in preset:
|
|
182
|
+
|
|
183
|
+
```yaml
|
|
184
|
+
gripper: 2f-85
|
|
185
|
+
gripper_config:
|
|
186
|
+
mass: 1.2
|
|
187
|
+
center_of_gravity: [0.0, 0.0, 0.07]
|
|
188
|
+
tcp_offset: [0.0, 0.0, 0.200, 0.0, 0.0, 0.0]
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### CLI Override Precedence
|
|
192
|
+
|
|
193
|
+
1. **CLI flags.** `urkit teach 192.168.1.50 --gripper none`
|
|
194
|
+
2. **Config file.** Values from `config.yaml`
|
|
195
|
+
3. **Built-in defaults.** `points.db`, no gripper, 0.5 m/s velocity
|
|
196
|
+
|
|
197
|
+
### Saving Config
|
|
198
|
+
|
|
199
|
+
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.
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
urkit teach 192.168.1.50 --gripper hand-e # test, then press Y
|
|
203
|
+
urkit teach # next time: reads from config
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Multiple workcells:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
urkit teach --config station_a.yaml # press Y to save
|
|
210
|
+
urkit teach --config station_b.yaml # separate config
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Programmatic
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from urkit import URRobot
|
|
217
|
+
|
|
218
|
+
robot = URRobot.from_config("config.yaml")
|
|
219
|
+
robot = URRobot.from_config("config.yaml", ip="10.0.0.50") # override IP
|
|
220
|
+
robot = URRobot.from_config({"robot_ip": "192.168.1.50", "gripper": "2f-85"}) # dict, no file
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
105
225
|
## Interactive CLI
|
|
106
226
|
|
|
107
227
|
URKit provides two CLI tools: **teach** for interactive robot control, and **points** for browsing saved waypoints.
|
|
@@ -192,7 +312,6 @@ All movement and orientation keys support **hold-to-repeat**.
|
|
|
192
312
|
<tr><td><code>V</code></td><td>Set position (mm)</td></tr>
|
|
193
313
|
<tr><td><code>6</code></td><td>Set speed (0-100)</td></tr>
|
|
194
314
|
<tr><td><code>7</code></td><td>Set force (0-100)</td></tr>
|
|
195
|
-
<tr><td colspan="3">Gripper line shows: `Connected 25.0mm (50%) F=100 S=100`</td></tr>
|
|
196
315
|
</table>
|
|
197
316
|
</td>
|
|
198
317
|
<td align="center" style="width:33%">
|
|
@@ -208,10 +327,11 @@ All movement and orientation keys support **hold-to-repeat**.
|
|
|
208
327
|
<td align="center" style="width:34%">
|
|
209
328
|
<table>
|
|
210
329
|
<tr><th>Key</th><th>Action</th></tr>
|
|
211
|
-
<tr><td><code>F</code></td><td>Freedrive (
|
|
330
|
+
<tr><td><code>F</code></td><td>Freedrive toggle (ALL ↔ XYZ)</td></tr>
|
|
331
|
+
<tr><td><code>3</code></td><td>Freedrive axis menu (toggle individual axes)</td></tr>
|
|
212
332
|
<tr><td><code>M</code></td><td>Toggle frame (BASE / TOOL)</td></tr>
|
|
213
333
|
<tr><td><code>N</code></td><td>Go To mode (Cartesian / Joint)</td></tr>
|
|
214
|
-
<tr><td><code>T</code></td><td>
|
|
334
|
+
<tr><td><code>T</code></td><td>Open TCP orient submenu (6 directions)</td></tr>
|
|
215
335
|
<tr><td><code>Y</code></td><td>Save config to file</td></tr>
|
|
216
336
|
<tr><td><code>ESC</code></td><td>Exit</td></tr>
|
|
217
337
|
</table>
|
|
@@ -224,10 +344,10 @@ All movement and orientation keys support **hold-to-repeat**.
|
|
|
224
344
|
The teach pendant shows live joint angles alongside TCP position and orientation:
|
|
225
345
|
|
|
226
346
|
```
|
|
227
|
-
Position
|
|
228
|
-
Orientation
|
|
229
|
-
Joints
|
|
230
|
-
|
|
347
|
+
Position X=+0.432 Y=+0.111 Z=+0.227
|
|
348
|
+
Orientation R=+131.3 P=-121.0 Y= +8.0
|
|
349
|
+
Joints J1=+150.0 J2=+ 20.0 J3=+160.0
|
|
350
|
+
J4=+ 50.0 J5=- 80.0 J6=+157.0
|
|
231
351
|
```
|
|
232
352
|
|
|
233
353
|
Joint angles color-code proximity to mechanical limits:
|
|
@@ -237,20 +357,20 @@ Joint angles color-code proximity to mechanical limits:
|
|
|
237
357
|
|
|
238
358
|
UR e-Series joint limits:
|
|
239
359
|
|
|
240
|
-
| Joint | Range |
|
|
241
|
-
|
|
242
|
-
| J1 (shoulder pan) | ±360° |
|
|
243
|
-
| J2 (shoulder lift) | ±360° |
|
|
244
|
-
| J3 (elbow) | ±
|
|
245
|
-
| J4 (wrist 1) | ±360° |
|
|
246
|
-
| J5 (wrist 2) | ±360° |
|
|
247
|
-
| J6 (wrist 3) | ±360° |
|
|
360
|
+
| Joint | Range |
|
|
361
|
+
|-------|-------|
|
|
362
|
+
| J1 (shoulder pan) | ±360° |
|
|
363
|
+
| J2 (shoulder lift) | ±360° |
|
|
364
|
+
| J3 (elbow) | ±360° |
|
|
365
|
+
| J4 (wrist 1) | ±360° |
|
|
366
|
+
| J5 (wrist 2) | ±360° |
|
|
367
|
+
| J6 (wrist 3) | ±360° |
|
|
368
|
+
|
|
248
369
|
|
|
249
|
-
Thresholds scale with each joint's range, so warning zones feel proportional across all joints.
|
|
250
370
|
|
|
251
371
|
### Safety
|
|
252
372
|
|
|
253
|
-
By default, **Go To** and **TCP
|
|
373
|
+
By default, **Go To** and **TCP orient** 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.
|
|
254
374
|
|
|
255
375
|
Delta movements (W/S/A/D/Q/E) use step-size-based velocities that scale with the speed slider set by the user.
|
|
256
376
|
|
|
@@ -289,12 +409,9 @@ robot = URRobot(
|
|
|
289
409
|
)
|
|
290
410
|
```
|
|
291
411
|
|
|
292
|
-
|
|
412
|
+
See [Configuration](#configuration) for `from_config()` usage.
|
|
293
413
|
|
|
294
|
-
|
|
295
|
-
robot = URRobot.from_config("config.yaml")
|
|
296
|
-
robot = URRobot.from_config("config.yaml", ip="10.0.0.50") # override IP
|
|
297
|
-
```
|
|
414
|
+
The 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.
|
|
298
415
|
|
|
299
416
|
### Grippers
|
|
300
417
|
|
|
@@ -302,21 +419,25 @@ Three built-in presets:
|
|
|
302
419
|
|
|
303
420
|
| Preset | Description |
|
|
304
421
|
|--------|-------------|
|
|
305
|
-
| `ROBOTIQ_HAND_E` | Robotiq
|
|
422
|
+
| `ROBOTIQ_HAND_E` | Robotiq Hand-E (2F-85-E) |
|
|
306
423
|
| `ROBOTIQ_2F_85` | Robotiq 2F-85 |
|
|
307
424
|
| `ROBOTIQ_2F_140` | Robotiq 2F-140 |
|
|
308
425
|
|
|
309
426
|
```python
|
|
310
427
|
robot.gripper.activate() # required before open/close (Robotiq only)
|
|
428
|
+
robot.gripper.deactivate() # deactivate (Robotiq only)
|
|
311
429
|
robot.gripper.is_activated() # check activation state
|
|
312
430
|
|
|
313
431
|
robot.gripper.open() # fully open (blocking by default)
|
|
314
432
|
robot.gripper.close() # fully closed, stops on contact
|
|
315
|
-
robot.gripper.open(wait=False) # non-blocking return
|
|
316
|
-
robot.gripper.set_position_mm(20) # 20mm open (Robotiq only, 0 = closed)
|
|
317
|
-
robot.gripper.set_position_percent(50) # 50% open (Robotiq only, 0 = open, 100 = closed)
|
|
433
|
+
robot.gripper.open(wait=False) # non-blocking return (waits for position)
|
|
434
|
+
robot.gripper.set_position_mm(20) # 20mm open (Robotiq only, 0 = fully closed)
|
|
435
|
+
robot.gripper.set_position_percent(50) # 50% open (Robotiq only, 0 = fully open, 100 = fully closed)
|
|
318
436
|
robot.gripper.set_force(50) # grip force: 0-100 (Robotiq only)
|
|
319
437
|
robot.gripper.set_speed(80) # movement speed: 0-100 (Robotiq only)
|
|
438
|
+
|
|
439
|
+
robot.gripper.get_position_mm() # last commanded position in mm
|
|
440
|
+
robot.gripper.max_travel_mm() # max finger travel (e.g. 85.0 for 2F-85)
|
|
320
441
|
```
|
|
321
442
|
|
|
322
443
|
Override preset values for custom fingers:
|
|
@@ -325,6 +446,19 @@ Override preset values for custom fingers:
|
|
|
325
446
|
robot = URRobot(ip="192.168.1.50", points="points.db", gripper=ROBOTIQ_HAND_E, max_mm=120)
|
|
326
447
|
```
|
|
327
448
|
|
|
449
|
+
Override physical properties (e.g., custom fingers or added hardware change the weight):
|
|
450
|
+
|
|
451
|
+
```python
|
|
452
|
+
robot = URRobot(
|
|
453
|
+
ip="192.168.1.50",
|
|
454
|
+
points="points.db",
|
|
455
|
+
gripper=ROBOTIQ_HAND_E,
|
|
456
|
+
mass=1.2, # override preset mass
|
|
457
|
+
center_of_gravity=[0.0, 0.0, 0.07], # override CoG
|
|
458
|
+
tcp_offset=[0.0, 0.0, 0.180, 0, 0, 0], # override TCP offset
|
|
459
|
+
)
|
|
460
|
+
```
|
|
461
|
+
|
|
328
462
|
#### Digital I/O Grippers
|
|
329
463
|
|
|
330
464
|
Robotiq grippers use a serial protocol over the robot's RS485 port. If you have a suction cup, solenoid, or any actuator controlled by a single digital output pin, use `DigitalGripperConfig` instead. It just turns that pin on (close) and off (open).
|
|
@@ -360,6 +494,7 @@ robot.move_to("pick") # linear move (default)
|
|
|
360
494
|
robot.move_to("pick", linear=False) # joint move
|
|
361
495
|
robot.move_to("pick", vel=1.0, acc=0.5) # override speed
|
|
362
496
|
robot.move_to("pick", asynchronous=True) # non-blocking, returns immediately
|
|
497
|
+
robot.move_to([0.5, 0, 0.3, 0, 0, 0]) # raw pose (no points DB needed)
|
|
363
498
|
```
|
|
364
499
|
|
|
365
500
|
- **Linear (moveL):** TCP moves in a straight line. Predictable path, slower near complex orientations.
|
|
@@ -377,20 +512,20 @@ robot.move_to("pick", asynchronous=True)
|
|
|
377
512
|
while robot.is_moving():
|
|
378
513
|
time.sleep(0.01)
|
|
379
514
|
|
|
380
|
-
# Or cancel mid-move
|
|
515
|
+
# Or cancel mid-move (see [Speed Control](#speed-control) for `stop()`)
|
|
381
516
|
robot.move_to("pick", asynchronous=True)
|
|
382
517
|
while robot.is_moving():
|
|
383
518
|
if should_cancel:
|
|
384
|
-
robot.stop()
|
|
519
|
+
robot.stop()
|
|
385
520
|
break
|
|
386
521
|
time.sleep(0.01)
|
|
387
522
|
```
|
|
388
523
|
|
|
389
|
-
**Teach pendant Go To** uses this pattern internally
|
|
524
|
+
**Teach pendant Go To** uses this pattern internally. Space cancels the move and returns to the menu.
|
|
390
525
|
|
|
391
526
|
#### Pose Format
|
|
392
527
|
|
|
393
|
-
A pose is `[x, y, z, rx, ry, rz]`: position in meters and orientation as a **rotation vector** (axis-angle in radians). This is not RPY (roll/pitch/yaw). The teach pendant displays RPY in degrees, which is a different representation. Values you see on the pendant won't match `
|
|
528
|
+
A pose is `[x, y, z, rx, ry, rz]`: position in meters and orientation as a **rotation vector** (axis-angle in radians). This is not RPY (roll/pitch/yaw). The teach pendant displays RPY in degrees, which is a different representation. Values you see on the pendant won't match `get_current_point()` directly.
|
|
394
529
|
|
|
395
530
|
#### Offsets
|
|
396
531
|
|
|
@@ -407,8 +542,8 @@ robot.move_to("pick", offset=[0, 0, 0.05, 0, 0.1, 0]) # full with rotation
|
|
|
407
542
|
Get a pose without moving. Useful for logging, comparisons, or custom motion:
|
|
408
543
|
|
|
409
544
|
```python
|
|
410
|
-
|
|
411
|
-
|
|
545
|
+
point = robot.get_point("pick")
|
|
546
|
+
point = robot.get_point("pick", offset=[0, 0, 0.05, 0, 0, 0]) # with offset
|
|
412
547
|
robot.move_to(pose) # move to the resolved pose later
|
|
413
548
|
```
|
|
414
549
|
|
|
@@ -424,11 +559,11 @@ robot.move_relative(delta_z=0.05) # 5cm along tool Z
|
|
|
424
559
|
- **BASE** (default): delta relative to robot base
|
|
425
560
|
- **TOOL**: delta relative to TCP orientation
|
|
426
561
|
|
|
427
|
-
#### IK Reference
|
|
562
|
+
#### IK Reference
|
|
428
563
|
|
|
429
564
|
**Problem:** When the robot has multiple valid joint configurations to reach the same TCP pose (e.g., elbow up vs. elbow down), it can unexpectedly flip its posture between moves. This is called an **IK ambiguity** and it causes "weird" movements where the robot takes a strange path or flips its wrist.
|
|
430
565
|
|
|
431
|
-
**Solution:** Set an **IK reference posture
|
|
566
|
+
**Solution:** Set an **IK reference posture**, a saved point that defines your preferred arm configuration. The robot then stays close to that posture for all moves.
|
|
432
567
|
|
|
433
568
|
```python
|
|
434
569
|
# 1. Put the robot in your preferred posture (e.g., "home")
|
|
@@ -436,7 +571,7 @@ robot.move_relative(delta_z=0.05) # 5cm along tool Z
|
|
|
436
571
|
# 3. Set as IK reference:
|
|
437
572
|
robot.ik_reference = "home"
|
|
438
573
|
|
|
439
|
-
# Now all moves stay close to that posture
|
|
574
|
+
# Now all moves stay close to that posture, no elbow flipping
|
|
440
575
|
robot.move_to("pick")
|
|
441
576
|
robot.move_to("place")
|
|
442
577
|
robot.move_relative(delta_z=-0.05)
|
|
@@ -449,7 +584,7 @@ robot_ip: 192.168.1.50
|
|
|
449
584
|
ik_reference: home # prevents weird elbow/wrist flips
|
|
450
585
|
```
|
|
451
586
|
|
|
452
|
-
**How it works:** The robot's inverse kinematics solver uses the reference posture as a bias (`qnear`). The TCP still reaches the exact same pose, but the arm configuration (elbow up/down, wrist orientation) stays consistent with your reference.
|
|
587
|
+
**How it works:** The robot's inverse kinematics solver uses the reference posture as a bias (`qnear`). The TCP still reaches the exact same pose, but the arm configuration (elbow up/down, wrist orientation) stays consistent with your reference. Under the hood, poses are resolved to joint angles and sent as `moveJ` instead of `moveL`.
|
|
453
588
|
|
|
454
589
|
**Per-move override:**
|
|
455
590
|
|
|
@@ -459,14 +594,17 @@ robot.move_to("weird_pose", ik_reference=None) # one move without it
|
|
|
459
594
|
robot.move_to("back", ik_reference="current") # use current joints
|
|
460
595
|
```
|
|
461
596
|
|
|
462
|
-
**When to use it:**
|
|
597
|
+
**When to use it:**
|
|
463
598
|
|
|
464
|
-
|
|
599
|
+
- **Use it** for sequences of positional moves between waypoints: pick/place paths, assembly sequences, anything where the robot travels between distant points. It prevents elbow/wrist flipping.
|
|
600
|
+
- **Don't use it** for rotation-heavy movements (orientation adjustments, fine-tuning angles). The `qnear` seed can push joints into unexpected configs for pure rotations, and you lose the controller's native Cartesian trajectory planner (lookahead, smoothing). Set `ik_reference=None` for these moves.
|
|
465
601
|
|
|
466
|
-
|
|
602
|
+
Default is `None` (controller handles IK natively). Set it globally when most of your moves are positional, and override per-move when you need rotations.
|
|
467
603
|
|
|
468
604
|
#### Point Management
|
|
469
605
|
|
|
606
|
+
Points are stored in the active TCP frame, so they work with any tool. Swap grippers and your saved points stay valid.
|
|
607
|
+
|
|
470
608
|
```python
|
|
471
609
|
robot.save_point("here")
|
|
472
610
|
robot.point_names() # ["home", "pick", "place"]
|
|
@@ -481,16 +619,36 @@ robot.import_points("backup.json")
|
|
|
481
619
|
```python
|
|
482
620
|
robot.move_relative(delta_y=0.01) # 1cm along Y
|
|
483
621
|
robot.move_relative(delta_z=0.05, frame=MoveFrame.TOOL) # 5cm along tool Z
|
|
484
|
-
robot.move_relative([0, 0.01, 0, 0, 0, 0])
|
|
622
|
+
robot.move_relative([0, 0.01, 0, 0, 0, 0]) # full 6-element delta
|
|
485
623
|
```
|
|
486
624
|
|
|
625
|
+
Individual delta parameters (`delta_x`, `delta_y`, `delta_z`, `delta_rx`, `delta_ry`, `delta_rz`) are mutually exclusive with the `delta` list. Use one or the other.
|
|
626
|
+
|
|
487
627
|
#### Sequences
|
|
488
628
|
|
|
489
629
|
```python
|
|
490
|
-
#
|
|
491
|
-
robot.move_sequence(
|
|
630
|
+
# Both styles work:
|
|
631
|
+
robot.move_sequence("a", "b", "c") # variadic
|
|
632
|
+
robot.move_sequence(["a", "b", "c"]) # list
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
`move_sequence` builds a path from all targets and executes it in a single call with blending. Requires at least 2 targets. Two modes:
|
|
636
|
+
|
|
637
|
+
**With `ik_reference`** (positional paths): All poses resolve to joints using chained inverse kinematics. The first pose resolves relative to the reference, the second relative to the first's resolved joints, and so on. Executed as a single `moveJ(path)` call. Keeps the arm configuration consistent (no elbow flipping):
|
|
638
|
+
|
|
639
|
+
```python
|
|
640
|
+
robot.ik_reference = "home"
|
|
641
|
+
robot.move_sequence(["a", "b", "c"], blend_radius=0.02) # 2cm blend
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
**Without `ik_reference`** (Cartesian path): Builds a blended `moveL(path)`. The controller handles IK natively, better for rotation-heavy sequences where `ik_reference` causes unexpected joint behavior:
|
|
645
|
+
|
|
646
|
+
```python
|
|
647
|
+
robot.move_sequence(["a", "b", "c"], blend_radius=0.02) # blended Cartesian path
|
|
492
648
|
```
|
|
493
649
|
|
|
650
|
+
Before sending any move, it checks that each pose has a valid IK solution. Unreachable poses raise `MotionError`. If a named point doesn't exist, it raises `PointError` instead of silently falling back.
|
|
651
|
+
|
|
494
652
|
#### Contact Detection
|
|
495
653
|
|
|
496
654
|
```python
|
|
@@ -513,8 +671,8 @@ robot.move_until_contact(speed_z=-0.02, timeout=10.0, max_distance=0.2)
|
|
|
513
671
|
for _ in range(3):
|
|
514
672
|
if robot.move_until_contact(speed_z=-0.02, timeout=10.0, max_distance=0.2):
|
|
515
673
|
break # contact detected
|
|
516
|
-
# No contact
|
|
517
|
-
robot.
|
|
674
|
+
# No contact, back off and retry
|
|
675
|
+
robot.move_relative(delta_z=0.01)
|
|
518
676
|
|
|
519
677
|
# Manual zero (e.g. before custom force-based logic)
|
|
520
678
|
robot.zero_ft_sensor()
|
|
@@ -532,40 +690,41 @@ robot.move_velocity([0, 0, -0.02, 0, 0, 0], duration=1.0)
|
|
|
532
690
|
from urkit import FreedriveMode
|
|
533
691
|
|
|
534
692
|
robot.enable_freedrive() # all 6 axes free
|
|
535
|
-
robot.enable_freedrive(FreedriveMode.XYZ) # linear axes
|
|
536
|
-
robot.enable_freedrive(FreedriveMode.ROTATION) # rotation only
|
|
693
|
+
robot.enable_freedrive(FreedriveMode.XYZ) # linear axes only (X, Y, Z)
|
|
694
|
+
robot.enable_freedrive(FreedriveMode.ROTATION) # rotation only (Roll, Pitch, Yaw)
|
|
695
|
+
robot.enable_freedrive([1, 1, 1, 1, 0, 0]) # custom: X+Y+Z+Roll+Pitch
|
|
537
696
|
robot.disable_freedrive() # disable before sending motion commands
|
|
538
697
|
robot.is_freedrive_active # check state
|
|
539
698
|
```
|
|
540
699
|
|
|
700
|
+
**Teach pendant:** Press `F` to toggle freedrive (ALL ↔ XYZ). Press `3` to open the axis selection menu arrow keys navigate, space toggles each axis on/off, enter applies.
|
|
701
|
+
|
|
541
702
|
#### Speed Control
|
|
542
703
|
|
|
543
704
|
```python
|
|
544
705
|
robot.stop() # stop current move immediately (stopL + stopJ)
|
|
545
706
|
robot.speed_stop() # stop velocity-controlled motion (not E-stop)
|
|
546
|
-
robot.
|
|
547
|
-
robot.
|
|
707
|
+
robot.speed_slider = 0.5 # 50% velocity cap
|
|
708
|
+
robot.speed_slider # read current slider (0.0-1.0)
|
|
548
709
|
```
|
|
549
710
|
|
|
550
711
|
The speed slider controls the pendant's speed multiplier. It's global, persistent, and affects all motion commands.
|
|
551
712
|
|
|
552
713
|
#### Changing Default Speed & Acceleration
|
|
553
714
|
|
|
554
|
-
Override the constructor defaults at runtime
|
|
715
|
+
Override the constructor defaults at runtime. All subsequent moves pick up the new values:
|
|
555
716
|
|
|
556
717
|
```python
|
|
557
|
-
robot.
|
|
718
|
+
robot.default_vel = 0.1 # slow for precision work
|
|
558
719
|
robot.move_to("insert")
|
|
559
|
-
robot.
|
|
720
|
+
robot.default_vel = 0.5 # back to normal
|
|
560
721
|
|
|
561
|
-
robot.
|
|
722
|
+
robot.default_acc = 0.05 # gentle acceleration
|
|
562
723
|
|
|
563
724
|
robot.default_vel # read current velocity (m/s)
|
|
564
725
|
robot.default_acc # read current acceleration (m/s²)
|
|
565
726
|
```
|
|
566
727
|
|
|
567
|
-
Soft warnings log when values exceed typical robot limits (> 2 m/s for velocity, > 6 m/s² for acceleration). The controller may clamp aggressive values based on payload and configuration.
|
|
568
|
-
|
|
569
728
|
#### Inverse Kinematics
|
|
570
729
|
|
|
571
730
|
```python
|
|
@@ -575,121 +734,124 @@ joints = robot.inverse_kinematics([0.5, 0, 0.3, 0, 0, 0])
|
|
|
575
734
|
### Telemetry
|
|
576
735
|
|
|
577
736
|
```python
|
|
578
|
-
pose = robot.
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
robot.
|
|
584
|
-
robot.is_protective_stopped()
|
|
585
|
-
robot.is_emergency_stopped()
|
|
586
|
-
robot.current_point() # {"pose": [...], "joints": [...]}
|
|
587
|
-
robot.is_at_pose(target) # bool — TCP within 1mm / 0.5°
|
|
588
|
-
robot.is_at_joints(target) # bool — all joints within 0.001 rad
|
|
737
|
+
pose = robot.get_current_point() # [x, y, z, rx, ry, rz]
|
|
738
|
+
pose = robot.get_current_point(offset_z=0.05) # with offset
|
|
739
|
+
joints = robot.get_joint_positions() # [j0..j5]
|
|
740
|
+
force = robot.get_tcp_force() # [fx, fy, fz, mx, my, mz]
|
|
741
|
+
mode = robot.get_robot_mode() # "REMOTE_CONTROL", "SERVOING", etc.
|
|
742
|
+
payload = robot.payload # kg
|
|
743
|
+
robot.is_protective_stopped() # bool
|
|
744
|
+
robot.is_emergency_stopped() # bool
|
|
589
745
|
```
|
|
590
746
|
|
|
591
|
-
|
|
747
|
+
#### Arrival Detection
|
|
748
|
+
|
|
749
|
+
`is_moving()` tracks the target pose or joints stored by `move_to()` and compares against the current position. Returns `False` when within tolerance:
|
|
592
750
|
|
|
593
751
|
```python
|
|
594
752
|
robot.move_to("pick", asynchronous=True)
|
|
595
753
|
|
|
596
|
-
#
|
|
754
|
+
# Wait until arrived (default tolerances: 2mm position, ~2° orientation)
|
|
597
755
|
while robot.is_moving():
|
|
598
756
|
time.sleep(0.01)
|
|
599
757
|
|
|
600
|
-
#
|
|
601
|
-
while
|
|
758
|
+
# Tighter tolerances
|
|
759
|
+
while robot.is_moving(position_tolerance=0.001, orientation_tolerance=0.017):
|
|
760
|
+
time.sleep(0.01)
|
|
761
|
+
|
|
762
|
+
# Joint moves, tighter joint tolerance
|
|
763
|
+
while robot.is_moving(joint_tolerance=0.001):
|
|
602
764
|
time.sleep(0.01)
|
|
603
765
|
```
|
|
604
766
|
|
|
767
|
+
For joint-space moves (when `ik_reference` is active or `linear=False`), `is_moving()` compares joint angles. For Cartesian moves, it compares TCP pose. When no target is set (e.g., after a relative move without `move_to`), it returns `False`.
|
|
768
|
+
|
|
769
|
+
---
|
|
770
|
+
|
|
771
|
+
## More API
|
|
772
|
+
|
|
773
|
+
Less common but useful when you need them.
|
|
774
|
+
|
|
605
775
|
### Digital I/O
|
|
606
776
|
|
|
777
|
+
Pins 0–7 are standard, 8–15 configurable, 16–17 tool.
|
|
778
|
+
|
|
607
779
|
```python
|
|
780
|
+
# Outputs
|
|
608
781
|
robot.set_digital_output(0, True)
|
|
609
782
|
robot.set_digital_outputs({0: True, 1: False, 8: True})
|
|
610
|
-
robot.set_digital_outputs(False) # clear all
|
|
783
|
+
robot.set_digital_outputs(False) # clear all pins 0–15
|
|
784
|
+
robot.get_digital_output(0) # read back output state
|
|
611
785
|
|
|
786
|
+
# Inputs
|
|
612
787
|
robot.get_digital_input(0)
|
|
613
|
-
robot.get_analog_input(0)
|
|
614
|
-
robot.get_tool_input(0)
|
|
615
|
-
|
|
616
788
|
robot.wait_for_input(0, True, timeout=10.0) # block until pin 0 goes high
|
|
617
|
-
```
|
|
618
|
-
|
|
619
|
-
---
|
|
620
789
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
790
|
+
# Analog
|
|
791
|
+
robot.get_analog_input(0) # read analog input (pin 0–1)
|
|
792
|
+
robot.get_analog_output(0) # read analog output (pin 0–1)
|
|
624
793
|
|
|
625
|
-
|
|
794
|
+
# Tool I/O
|
|
795
|
+
robot.get_tool_input(0) # tool digital input (pin 0–1)
|
|
796
|
+
robot.get_tool_output(0) # tool digital output (pin 0–1)
|
|
797
|
+
```
|
|
626
798
|
|
|
627
|
-
|
|
628
|
-
1. Explicit path via `--config` flag or `load_config("path")`
|
|
629
|
-
2. Project root (where `src/urkit` lives)
|
|
630
|
-
3. Current working directory
|
|
799
|
+
### Geometry
|
|
631
800
|
|
|
632
|
-
|
|
801
|
+
Conversion utilities for rotation vectors, quaternions, and RPY angles (rxyz convention, same as UR teach pendant):
|
|
633
802
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
803
|
+
```python
|
|
804
|
+
from urkit import (
|
|
805
|
+
orient_tcp,
|
|
806
|
+
orient_tcp_down,
|
|
807
|
+
quat_to_rotvec,
|
|
808
|
+
quat_to_rpy,
|
|
809
|
+
rpy_to_quat,
|
|
810
|
+
rotvec_to_quat,
|
|
811
|
+
)
|
|
643
812
|
|
|
644
|
-
|
|
813
|
+
# Orient TCP along an arbitrary direction (minimal rotation)
|
|
814
|
+
new_pose = orient_tcp(current_pose, [0, 0, -1]) # point down
|
|
815
|
+
new_pose = orient_tcp(current_pose, [1, 0, 0]) # point forward
|
|
645
816
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
gripper_config:
|
|
649
|
-
pin: 3
|
|
650
|
-
close_on_high: true
|
|
651
|
-
```
|
|
817
|
+
# Orient TCP straight down (convenience wrapper)
|
|
818
|
+
new_pose = orient_tcp_down(current_pose)
|
|
652
819
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
820
|
+
# Rotation conversions
|
|
821
|
+
q = rotvec_to_quat([0, 0, 1.57]) # rotvec → quaternion (x, y, z, w)
|
|
822
|
+
rv = quat_to_rotvec(q) # quaternion → rotvec
|
|
823
|
+
rpy = quat_to_rpy(q) # quaternion → RPY (radians)
|
|
824
|
+
q = rpy_to_quat(0, 0, 1.57) # RPY → quaternion
|
|
658
825
|
```
|
|
659
826
|
|
|
660
|
-
###
|
|
661
|
-
|
|
662
|
-
1. **CLI flags.** `urkit teach 192.168.1.50 --gripper none`
|
|
663
|
-
2. **Config file.** Values from `config.yaml`
|
|
664
|
-
3. **Built-in defaults.** `points.db`, no gripper, 0.5 m/s velocity
|
|
827
|
+
### Robot Lifecycle
|
|
665
828
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
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.
|
|
829
|
+
Manual power and brake control via the Dashboard:
|
|
669
830
|
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
|
|
831
|
+
```python
|
|
832
|
+
robot.power_on() # power on (skips if already on)
|
|
833
|
+
robot.release_brakes() # release brakes (enable control)
|
|
834
|
+
robot.recover() # clear protective stop
|
|
835
|
+
robot.power_off() # power off
|
|
673
836
|
```
|
|
674
837
|
|
|
675
|
-
|
|
838
|
+
The constructor handles all of this automatically. Use these methods when you need fine-grained control (e.g., recovering from a safety stop without recreating the robot).
|
|
676
839
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
840
|
+
TCP and payload can be set manually (gripper presets do this automatically):
|
|
841
|
+
|
|
842
|
+
```python
|
|
843
|
+
robot.tcp_offset = [0, 0, 0.15, 0, 0, 0]
|
|
844
|
+
robot.set_payload(1.5, [0, 0, 0.05]) # mass (kg), center of gravity [x, y, z]
|
|
680
845
|
```
|
|
681
846
|
|
|
682
|
-
|
|
847
|
+
Clean shutdown:
|
|
683
848
|
|
|
684
849
|
```python
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
config = load_config() # auto-resolve
|
|
688
|
-
config = load_config("/path/to/my.yaml") # explicit path
|
|
689
|
-
path = resolve_config() # returns Path or None
|
|
690
|
-
robot = URRobot.from_config({"robot_ip": "192.168.1.50", "gripper": "2f-85"})
|
|
850
|
+
robot.disconnect() # close RTDE, Dashboard, points DB, gripper
|
|
691
851
|
```
|
|
692
852
|
|
|
853
|
+
`disconnect()` is called automatically on garbage collection. Call it explicitly to release resources immediately.
|
|
854
|
+
|
|
693
855
|
---
|
|
694
856
|
|
|
695
857
|
## Advanced
|
|
@@ -713,8 +875,6 @@ robot.connection_lost # bool: check if RTDE dropped
|
|
|
713
875
|
robot.reconnect_rtde() # reconnect after a drop
|
|
714
876
|
```
|
|
715
877
|
|
|
716
|
-
`disconnect()` is called automatically when the robot object is garbage collected.
|
|
717
|
-
|
|
718
878
|
### Error Handling
|
|
719
879
|
|
|
720
880
|
```python
|
|
@@ -742,13 +902,8 @@ Common runtime errors:
|
|
|
742
902
|
|
|
743
903
|
When the robot enters protective stop or the RTDE connection drops, motion commands raise `URKitConnectionError` and the program should exit. The CLI handles this automatically.
|
|
744
904
|
|
|
745
|
-
### Connection Notes
|
|
746
|
-
|
|
747
|
-
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.
|
|
748
|
-
|
|
749
905
|
---
|
|
750
906
|
|
|
751
907
|
## Changelog
|
|
752
908
|
|
|
753
909
|
See [CHANGELOG.md](CHANGELOG.md) for the full version history.
|
|
754
|
-
|