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.
- urkit-0.1.0/PKG-INFO +612 -0
- urkit-0.1.0/README.md +587 -0
- urkit-0.1.0/pyproject.toml +69 -0
- urkit-0.1.0/setup.cfg +4 -0
- urkit-0.1.0/src/urkit/__init__.py +99 -0
- urkit-0.1.0/src/urkit/__main__.py +116 -0
- urkit-0.1.0/src/urkit/cli/__init__.py +6 -0
- urkit-0.1.0/src/urkit/cli/colors.py +99 -0
- urkit-0.1.0/src/urkit/cli/connection_monitor.py +154 -0
- urkit-0.1.0/src/urkit/cli/points.py +293 -0
- urkit-0.1.0/src/urkit/cli/teach.py +1181 -0
- urkit-0.1.0/src/urkit/config.py +78 -0
- urkit-0.1.0/src/urkit/connection.py +580 -0
- urkit-0.1.0/src/urkit/exceptions.py +51 -0
- urkit-0.1.0/src/urkit/geometry.py +413 -0
- urkit-0.1.0/src/urkit/gripper/__init__.py +36 -0
- urkit-0.1.0/src/urkit/gripper/base.py +116 -0
- urkit-0.1.0/src/urkit/gripper/digital.py +140 -0
- urkit-0.1.0/src/urkit/gripper/presets.py +113 -0
- urkit-0.1.0/src/urkit/gripper/robotiq.py +322 -0
- urkit-0.1.0/src/urkit/gripper/robotiq_preamble.py +1356 -0
- urkit-0.1.0/src/urkit/io.py +311 -0
- urkit-0.1.0/src/urkit/motion.py +504 -0
- urkit-0.1.0/src/urkit/points.py +277 -0
- urkit-0.1.0/src/urkit/robot.py +1510 -0
- urkit-0.1.0/src/urkit/telemetry.py +177 -0
- urkit-0.1.0/src/urkit.egg-info/PKG-INFO +612 -0
- urkit-0.1.0/src/urkit.egg-info/SOURCES.txt +37 -0
- urkit-0.1.0/src/urkit.egg-info/dependency_links.txt +1 -0
- urkit-0.1.0/src/urkit.egg-info/entry_points.txt +2 -0
- urkit-0.1.0/src/urkit.egg-info/requires.txt +4 -0
- urkit-0.1.0/src/urkit.egg-info/top_level.txt +1 -0
- urkit-0.1.0/tests/test_exceptions.py +92 -0
- urkit-0.1.0/tests/test_geometry.py +187 -0
- urkit-0.1.0/tests/test_gripper.py +167 -0
- urkit-0.1.0/tests/test_gripper_factory.py +184 -0
- urkit-0.1.0/tests/test_gripper_presets.py +152 -0
- urkit-0.1.0/tests/test_points.py +356 -0
- 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
|
+
```
|