urkit 0.3.3__tar.gz → 0.3.5__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.3 → urkit-0.3.5}/PKG-INFO +20 -19
- {urkit-0.3.3 → urkit-0.3.5}/README.md +19 -18
- {urkit-0.3.3 → urkit-0.3.5}/pyproject.toml +1 -1
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/__init__.py +2 -2
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/__main__.py +4 -4
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/cli/teach.py +12 -10
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/gripper/__init__.py +2 -2
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/gripper/base.py +23 -8
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/gripper/digital.py +13 -2
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/gripper/presets.py +3 -3
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/gripper/robotiq.py +37 -2
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/robot.py +44 -6
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit.egg-info/PKG-INFO +20 -19
- {urkit-0.3.3 → urkit-0.3.5}/tests/test_gripper.py +13 -13
- {urkit-0.3.3 → urkit-0.3.5}/tests/test_robot_integration.py +1 -1
- {urkit-0.3.3 → urkit-0.3.5}/setup.cfg +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/cli/__init__.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/cli/colors.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/cli/connection_monitor.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/cli/points.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/config.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/connection.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/exceptions.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/geometry.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/gripper/robotiq_preamble.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/io.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/motion.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/points.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit/telemetry.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit.egg-info/SOURCES.txt +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit.egg-info/dependency_links.txt +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit.egg-info/entry_points.txt +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit.egg-info/requires.txt +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/src/urkit.egg-info/top_level.txt +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/tests/test_exceptions.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/tests/test_geometry.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/tests/test_gripper_factory.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/tests/test_gripper_presets.py +0 -0
- {urkit-0.3.3 → urkit-0.3.5}/tests/test_points.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: urkit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: Universal Robots e-Series control toolkit built on ur_rtde
|
|
5
5
|
Author: URKit Contributors
|
|
6
6
|
License: MIT
|
|
@@ -69,9 +69,9 @@ Requires Python 3.8+ and a Universal Robots e-Series (UR3e to UR30).
|
|
|
69
69
|
### Robot Setup (one-time)
|
|
70
70
|
|
|
71
71
|
1. **Network**: `☰` → `System` → `System` → `Network`. Set a static IP on the robot and a matching one on your PC. Both addresses must share the same first three octets (the network), with a different last octet (the host). For example:
|
|
72
|
-
- **Robot**: `
|
|
73
|
-
- **PC**: `
|
|
74
|
-
- Verify with `ping
|
|
72
|
+
- **Robot**: `192.168.1.50` / Subnet `255.255.255.0`
|
|
73
|
+
- **PC**: `192.168.1.1` / Netmask `255.255.255.0`
|
|
74
|
+
- Verify with `ping 192.168.1.50`. Connect via direct Ethernet cable or a switch.
|
|
75
75
|
2. **Remote Control**: `☰` → `System` → `Remote Control`: Enable. Press the remote/local button on the pendant.
|
|
76
76
|
3. **Security**: `☰` → `Security` → `Services`: enable RTDE and disable EtherNet/IP, PROFINET, or MODBUS if they're claiming RTDE registers. Save and restart.
|
|
77
77
|
|
|
@@ -84,7 +84,7 @@ If you have a Robotiq gripper, install the **Robotiq Gripper Control** URCap fir
|
|
|
84
84
|
```python
|
|
85
85
|
from urkit import URRobot, ROBOTIQ_HAND_E # or ROBOTIQ_2F_85, ROBOTIQ_2F_140, or gripper=None
|
|
86
86
|
|
|
87
|
-
robot = URRobot(ip="
|
|
87
|
+
robot = URRobot(ip="192.168.1.50", points="points.db", gripper=ROBOTIQ_HAND_E)
|
|
88
88
|
robot.gripper.activate()
|
|
89
89
|
|
|
90
90
|
robot.move_to("home")
|
|
@@ -111,7 +111,7 @@ URKit provides two CLI tools: **teach** for interactive robot control, and **poi
|
|
|
111
111
|
The interactive teach pendant for moving the robot, saving points, and checking telemetry:
|
|
112
112
|
|
|
113
113
|
```bash
|
|
114
|
-
urkit teach
|
|
114
|
+
urkit teach 192.168.1.50 # with robot IP
|
|
115
115
|
urkit teach # reads IP from config.yaml
|
|
116
116
|
```
|
|
117
117
|
|
|
@@ -192,7 +192,7 @@ All movement and orientation keys support **hold-to-repeat**.
|
|
|
192
192
|
<tr><td><code>V</code></td><td>Set position (mm)</td></tr>
|
|
193
193
|
<tr><td><code>6</code></td><td>Set speed (0-100)</td></tr>
|
|
194
194
|
<tr><td><code>7</code></td><td>Set force (0-100)</td></tr>
|
|
195
|
-
<tr><td colspan="3">Gripper line shows: `Connected
|
|
195
|
+
<tr><td colspan="3">Gripper line shows: `Connected 25.0mm (50%) F=100 S=100`</td></tr>
|
|
196
196
|
</table>
|
|
197
197
|
</td>
|
|
198
198
|
<td align="center" style="width:33%">
|
|
@@ -257,7 +257,7 @@ Delta movements (W/S/A/D/Q/E) use step-size-based velocities that scale with the
|
|
|
257
257
|
To disable the slow default and use full speed, pass `--expert` or set `expert_mode: true` in your config:
|
|
258
258
|
|
|
259
259
|
```bash
|
|
260
|
-
urkit teach
|
|
260
|
+
urkit teach 192.168.1.50 --expert
|
|
261
261
|
```
|
|
262
262
|
|
|
263
263
|
```yaml
|
|
@@ -274,14 +274,14 @@ expert_mode: true
|
|
|
274
274
|
```python
|
|
275
275
|
from urkit import URRobot, ROBOTIQ_HAND_E
|
|
276
276
|
|
|
277
|
-
robot = URRobot(ip="
|
|
277
|
+
robot = URRobot(ip="192.168.1.50", points="points.db", gripper=ROBOTIQ_HAND_E)
|
|
278
278
|
```
|
|
279
279
|
|
|
280
280
|
With custom motion defaults:
|
|
281
281
|
|
|
282
282
|
```python
|
|
283
283
|
robot = URRobot(
|
|
284
|
-
ip="
|
|
284
|
+
ip="192.168.1.50",
|
|
285
285
|
points="points.db",
|
|
286
286
|
gripper=ROBOTIQ_HAND_E,
|
|
287
287
|
default_vel=0.5, # m/s
|
|
@@ -313,7 +313,8 @@ robot.gripper.is_activated() # check activation state
|
|
|
313
313
|
robot.gripper.open() # fully open (blocking by default)
|
|
314
314
|
robot.gripper.close() # fully closed, stops on contact
|
|
315
315
|
robot.gripper.open(wait=False) # non-blocking return
|
|
316
|
-
robot.gripper.
|
|
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)
|
|
317
318
|
robot.gripper.set_force(50) # grip force: 0-100 (Robotiq only)
|
|
318
319
|
robot.gripper.set_speed(80) # movement speed: 0-100 (Robotiq only)
|
|
319
320
|
```
|
|
@@ -321,7 +322,7 @@ robot.gripper.set_speed(80) # movement speed: 0-100 (Robotiq only)
|
|
|
321
322
|
Override preset values for custom fingers:
|
|
322
323
|
|
|
323
324
|
```python
|
|
324
|
-
robot = URRobot(ip="
|
|
325
|
+
robot = URRobot(ip="192.168.1.50", points="points.db", gripper=ROBOTIQ_HAND_E, max_mm=120)
|
|
325
326
|
```
|
|
326
327
|
|
|
327
328
|
#### Digital I/O Grippers
|
|
@@ -332,7 +333,7 @@ Robotiq grippers use a serial protocol over the robot's RS485 port. If you have
|
|
|
332
333
|
from urkit import URRobot, DigitalGripperConfig
|
|
333
334
|
|
|
334
335
|
robot = URRobot(
|
|
335
|
-
ip="
|
|
336
|
+
ip="192.168.1.50",
|
|
336
337
|
points="points.db",
|
|
337
338
|
gripper=DigitalGripperConfig(pin=3), # pin 3 goes high = close
|
|
338
339
|
)
|
|
@@ -348,7 +349,7 @@ robot.gripper.close() # turn pin 3 on
|
|
|
348
349
|
The points database is optional. Create a robot without one and attach later:
|
|
349
350
|
|
|
350
351
|
```python
|
|
351
|
-
robot = URRobot(ip="
|
|
352
|
+
robot = URRobot(ip="192.168.1.50")
|
|
352
353
|
robot.points_db = "points.db"
|
|
353
354
|
```
|
|
354
355
|
|
|
@@ -552,7 +553,7 @@ URKit searches for `config.yaml` in this order:
|
|
|
552
553
|
|
|
553
554
|
| Key | Description | Example |
|
|
554
555
|
|-----|-------------|---------|
|
|
555
|
-
| `robot_ip` | Robot IP address | `
|
|
556
|
+
| `robot_ip` | Robot IP address | `192.168.1.50` |
|
|
556
557
|
| `points_path` | Path to SQLite points database | `points.db` |
|
|
557
558
|
| `gripper` | Gripper preset name | `hand-e`, `2f-85`, `2f-140`, `digital` |
|
|
558
559
|
| `default_vel` | Default linear velocity (m/s) | `0.5` |
|
|
@@ -577,7 +578,7 @@ gripper_config:
|
|
|
577
578
|
|
|
578
579
|
### CLI Override Precedence
|
|
579
580
|
|
|
580
|
-
1. **CLI flags.** `urkit teach
|
|
581
|
+
1. **CLI flags.** `urkit teach 192.168.1.50 --gripper none`
|
|
581
582
|
2. **Config file.** Values from `config.yaml`
|
|
582
583
|
3. **Built-in defaults.** `points.db`, no gripper, 0.5 m/s velocity
|
|
583
584
|
|
|
@@ -586,7 +587,7 @@ gripper_config:
|
|
|
586
587
|
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.
|
|
587
588
|
|
|
588
589
|
```bash
|
|
589
|
-
urkit teach
|
|
590
|
+
urkit teach 192.168.1.50 --gripper hand-e # test, then press Y
|
|
590
591
|
urkit teach # next time: reads from config
|
|
591
592
|
```
|
|
592
593
|
|
|
@@ -605,7 +606,7 @@ from urkit import load_config, resolve_config
|
|
|
605
606
|
config = load_config() # auto-resolve
|
|
606
607
|
config = load_config("/path/to/my.yaml") # explicit path
|
|
607
608
|
path = resolve_config() # returns Path or None
|
|
608
|
-
robot = URRobot.from_config({"robot_ip": "
|
|
609
|
+
robot = URRobot.from_config({"robot_ip": "192.168.1.50", "gripper": "2f-85"})
|
|
609
610
|
```
|
|
610
611
|
|
|
611
612
|
---
|
|
@@ -639,7 +640,7 @@ robot.reconnect_rtde() # reconnect after a drop
|
|
|
639
640
|
from urkit import URKitError, MotionError, PointError
|
|
640
641
|
|
|
641
642
|
try:
|
|
642
|
-
robot = URRobot(ip="
|
|
643
|
+
robot = URRobot(ip="192.168.1.50", points="points.db")
|
|
643
644
|
except RobotNotInRemoteModeError:
|
|
644
645
|
print("Enable remote control on the teach pendant!")
|
|
645
646
|
except RtdeRegisterConflictError:
|
|
@@ -43,9 +43,9 @@ Requires Python 3.8+ and a Universal Robots e-Series (UR3e to UR30).
|
|
|
43
43
|
### Robot Setup (one-time)
|
|
44
44
|
|
|
45
45
|
1. **Network**: `☰` → `System` → `System` → `Network`. Set a static IP on the robot and a matching one on your PC. Both addresses must share the same first three octets (the network), with a different last octet (the host). For example:
|
|
46
|
-
- **Robot**: `
|
|
47
|
-
- **PC**: `
|
|
48
|
-
- Verify with `ping
|
|
46
|
+
- **Robot**: `192.168.1.50` / Subnet `255.255.255.0`
|
|
47
|
+
- **PC**: `192.168.1.1` / Netmask `255.255.255.0`
|
|
48
|
+
- Verify with `ping 192.168.1.50`. Connect via direct Ethernet cable or a switch.
|
|
49
49
|
2. **Remote Control**: `☰` → `System` → `Remote Control`: Enable. Press the remote/local button on the pendant.
|
|
50
50
|
3. **Security**: `☰` → `Security` → `Services`: enable RTDE and disable EtherNet/IP, PROFINET, or MODBUS if they're claiming RTDE registers. Save and restart.
|
|
51
51
|
|
|
@@ -58,7 +58,7 @@ If you have a Robotiq gripper, install the **Robotiq Gripper Control** URCap fir
|
|
|
58
58
|
```python
|
|
59
59
|
from urkit import URRobot, ROBOTIQ_HAND_E # or ROBOTIQ_2F_85, ROBOTIQ_2F_140, or gripper=None
|
|
60
60
|
|
|
61
|
-
robot = URRobot(ip="
|
|
61
|
+
robot = URRobot(ip="192.168.1.50", points="points.db", gripper=ROBOTIQ_HAND_E)
|
|
62
62
|
robot.gripper.activate()
|
|
63
63
|
|
|
64
64
|
robot.move_to("home")
|
|
@@ -85,7 +85,7 @@ URKit provides two CLI tools: **teach** for interactive robot control, and **poi
|
|
|
85
85
|
The interactive teach pendant for moving the robot, saving points, and checking telemetry:
|
|
86
86
|
|
|
87
87
|
```bash
|
|
88
|
-
urkit teach
|
|
88
|
+
urkit teach 192.168.1.50 # with robot IP
|
|
89
89
|
urkit teach # reads IP from config.yaml
|
|
90
90
|
```
|
|
91
91
|
|
|
@@ -166,7 +166,7 @@ All movement and orientation keys support **hold-to-repeat**.
|
|
|
166
166
|
<tr><td><code>V</code></td><td>Set position (mm)</td></tr>
|
|
167
167
|
<tr><td><code>6</code></td><td>Set speed (0-100)</td></tr>
|
|
168
168
|
<tr><td><code>7</code></td><td>Set force (0-100)</td></tr>
|
|
169
|
-
<tr><td colspan="3">Gripper line shows: `Connected
|
|
169
|
+
<tr><td colspan="3">Gripper line shows: `Connected 25.0mm (50%) F=100 S=100`</td></tr>
|
|
170
170
|
</table>
|
|
171
171
|
</td>
|
|
172
172
|
<td align="center" style="width:33%">
|
|
@@ -231,7 +231,7 @@ Delta movements (W/S/A/D/Q/E) use step-size-based velocities that scale with the
|
|
|
231
231
|
To disable the slow default and use full speed, pass `--expert` or set `expert_mode: true` in your config:
|
|
232
232
|
|
|
233
233
|
```bash
|
|
234
|
-
urkit teach
|
|
234
|
+
urkit teach 192.168.1.50 --expert
|
|
235
235
|
```
|
|
236
236
|
|
|
237
237
|
```yaml
|
|
@@ -248,14 +248,14 @@ expert_mode: true
|
|
|
248
248
|
```python
|
|
249
249
|
from urkit import URRobot, ROBOTIQ_HAND_E
|
|
250
250
|
|
|
251
|
-
robot = URRobot(ip="
|
|
251
|
+
robot = URRobot(ip="192.168.1.50", points="points.db", gripper=ROBOTIQ_HAND_E)
|
|
252
252
|
```
|
|
253
253
|
|
|
254
254
|
With custom motion defaults:
|
|
255
255
|
|
|
256
256
|
```python
|
|
257
257
|
robot = URRobot(
|
|
258
|
-
ip="
|
|
258
|
+
ip="192.168.1.50",
|
|
259
259
|
points="points.db",
|
|
260
260
|
gripper=ROBOTIQ_HAND_E,
|
|
261
261
|
default_vel=0.5, # m/s
|
|
@@ -287,7 +287,8 @@ robot.gripper.is_activated() # check activation state
|
|
|
287
287
|
robot.gripper.open() # fully open (blocking by default)
|
|
288
288
|
robot.gripper.close() # fully closed, stops on contact
|
|
289
289
|
robot.gripper.open(wait=False) # non-blocking return
|
|
290
|
-
robot.gripper.
|
|
290
|
+
robot.gripper.set_position_mm(20) # 20mm open (Robotiq only, 0 = closed)
|
|
291
|
+
robot.gripper.set_position_percent(50) # 50% open (Robotiq only, 0 = open, 100 = closed)
|
|
291
292
|
robot.gripper.set_force(50) # grip force: 0-100 (Robotiq only)
|
|
292
293
|
robot.gripper.set_speed(80) # movement speed: 0-100 (Robotiq only)
|
|
293
294
|
```
|
|
@@ -295,7 +296,7 @@ robot.gripper.set_speed(80) # movement speed: 0-100 (Robotiq only)
|
|
|
295
296
|
Override preset values for custom fingers:
|
|
296
297
|
|
|
297
298
|
```python
|
|
298
|
-
robot = URRobot(ip="
|
|
299
|
+
robot = URRobot(ip="192.168.1.50", points="points.db", gripper=ROBOTIQ_HAND_E, max_mm=120)
|
|
299
300
|
```
|
|
300
301
|
|
|
301
302
|
#### Digital I/O Grippers
|
|
@@ -306,7 +307,7 @@ Robotiq grippers use a serial protocol over the robot's RS485 port. If you have
|
|
|
306
307
|
from urkit import URRobot, DigitalGripperConfig
|
|
307
308
|
|
|
308
309
|
robot = URRobot(
|
|
309
|
-
ip="
|
|
310
|
+
ip="192.168.1.50",
|
|
310
311
|
points="points.db",
|
|
311
312
|
gripper=DigitalGripperConfig(pin=3), # pin 3 goes high = close
|
|
312
313
|
)
|
|
@@ -322,7 +323,7 @@ robot.gripper.close() # turn pin 3 on
|
|
|
322
323
|
The points database is optional. Create a robot without one and attach later:
|
|
323
324
|
|
|
324
325
|
```python
|
|
325
|
-
robot = URRobot(ip="
|
|
326
|
+
robot = URRobot(ip="192.168.1.50")
|
|
326
327
|
robot.points_db = "points.db"
|
|
327
328
|
```
|
|
328
329
|
|
|
@@ -526,7 +527,7 @@ URKit searches for `config.yaml` in this order:
|
|
|
526
527
|
|
|
527
528
|
| Key | Description | Example |
|
|
528
529
|
|-----|-------------|---------|
|
|
529
|
-
| `robot_ip` | Robot IP address | `
|
|
530
|
+
| `robot_ip` | Robot IP address | `192.168.1.50` |
|
|
530
531
|
| `points_path` | Path to SQLite points database | `points.db` |
|
|
531
532
|
| `gripper` | Gripper preset name | `hand-e`, `2f-85`, `2f-140`, `digital` |
|
|
532
533
|
| `default_vel` | Default linear velocity (m/s) | `0.5` |
|
|
@@ -551,7 +552,7 @@ gripper_config:
|
|
|
551
552
|
|
|
552
553
|
### CLI Override Precedence
|
|
553
554
|
|
|
554
|
-
1. **CLI flags.** `urkit teach
|
|
555
|
+
1. **CLI flags.** `urkit teach 192.168.1.50 --gripper none`
|
|
555
556
|
2. **Config file.** Values from `config.yaml`
|
|
556
557
|
3. **Built-in defaults.** `points.db`, no gripper, 0.5 m/s velocity
|
|
557
558
|
|
|
@@ -560,7 +561,7 @@ gripper_config:
|
|
|
560
561
|
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.
|
|
561
562
|
|
|
562
563
|
```bash
|
|
563
|
-
urkit teach
|
|
564
|
+
urkit teach 192.168.1.50 --gripper hand-e # test, then press Y
|
|
564
565
|
urkit teach # next time: reads from config
|
|
565
566
|
```
|
|
566
567
|
|
|
@@ -579,7 +580,7 @@ from urkit import load_config, resolve_config
|
|
|
579
580
|
config = load_config() # auto-resolve
|
|
580
581
|
config = load_config("/path/to/my.yaml") # explicit path
|
|
581
582
|
path = resolve_config() # returns Path or None
|
|
582
|
-
robot = URRobot.from_config({"robot_ip": "
|
|
583
|
+
robot = URRobot.from_config({"robot_ip": "192.168.1.50", "gripper": "2f-85"})
|
|
583
584
|
```
|
|
584
585
|
|
|
585
586
|
---
|
|
@@ -613,7 +614,7 @@ robot.reconnect_rtde() # reconnect after a drop
|
|
|
613
614
|
from urkit import URKitError, MotionError, PointError
|
|
614
615
|
|
|
615
616
|
try:
|
|
616
|
-
robot = URRobot(ip="
|
|
617
|
+
robot = URRobot(ip="192.168.1.50", points="points.db")
|
|
617
618
|
except RobotNotInRemoteModeError:
|
|
618
619
|
print("Enable remote control on the teach pendant!")
|
|
619
620
|
except RtdeRegisterConflictError:
|
|
@@ -10,7 +10,7 @@ Quick start::
|
|
|
10
10
|
from urkit import URRobot, ROBOTIQ_HAND_E
|
|
11
11
|
|
|
12
12
|
robot = URRobot(
|
|
13
|
-
ip="
|
|
13
|
+
ip="192.168.1.50",
|
|
14
14
|
points="points.db",
|
|
15
15
|
gripper=ROBOTIQ_HAND_E,
|
|
16
16
|
)
|
|
@@ -24,7 +24,7 @@ Quick start::
|
|
|
24
24
|
|
|
25
25
|
from __future__ import annotations
|
|
26
26
|
|
|
27
|
-
__version__ = "0.3.
|
|
27
|
+
__version__ = "0.3.5"
|
|
28
28
|
|
|
29
29
|
from urkit.config import load_config, resolve_config
|
|
30
30
|
from urkit.exceptions import (
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""urkit CLI entry point.
|
|
2
2
|
|
|
3
3
|
Usage:
|
|
4
|
-
urkit teach
|
|
5
|
-
urkit teach
|
|
4
|
+
urkit teach 192.168.1.50
|
|
5
|
+
urkit teach 192.168.1.50 --gripper=hand-e
|
|
6
6
|
urkit points list
|
|
7
7
|
urkit points list --filter "pick*"
|
|
8
8
|
"""
|
|
@@ -41,8 +41,8 @@ def main() -> None:
|
|
|
41
41
|
"--gripper",
|
|
42
42
|
type=str,
|
|
43
43
|
default=None,
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
help="Gripper preset (overrides config file). Use 'none' to disable gripper. "
|
|
45
|
+
"Accepts: 2f-85, 2f-140, hand-e, digital, none (case-insensitive, _ or -)",
|
|
46
46
|
)
|
|
47
47
|
teach_parser.add_argument(
|
|
48
48
|
"--gripper-pin",
|
|
@@ -4,9 +4,9 @@ A single terminal-based UI for manual robot control, point management,
|
|
|
4
4
|
and freedrive — matching the pattern from robot_mover's point_saver.cpp.
|
|
5
5
|
|
|
6
6
|
Usage:
|
|
7
|
-
urkit
|
|
7
|
+
urkit 192.168.1.50 # connect with IP
|
|
8
8
|
urkit # reads robot_ip from config
|
|
9
|
-
urkit -v
|
|
9
|
+
urkit -v 192.168.1.50 # verbose (debug boot commands)
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
@@ -351,7 +351,7 @@ def _draw_screen(
|
|
|
351
351
|
if payload == 0:
|
|
352
352
|
tcp_label = f"{dim('0.0kg (no payload)')}"
|
|
353
353
|
else:
|
|
354
|
-
tcp_label = green(f"{payload:.
|
|
354
|
+
tcp_label = green(f"{payload:.3f}kg")
|
|
355
355
|
lines.append(f" {blue('Payload:'.ljust(lw))} {tcp_label}")
|
|
356
356
|
except Exception:
|
|
357
357
|
pass
|
|
@@ -1229,13 +1229,12 @@ def _teach_pendant(
|
|
|
1229
1229
|
|
|
1230
1230
|
elif key == "v":
|
|
1231
1231
|
if robot.gripper:
|
|
1232
|
-
|
|
1233
|
-
prompt = f" Gripper position (0-{max_mm} mm): " if max_mm is not None else " Gripper position (mm): "
|
|
1232
|
+
prompt = " Gripper position (0-100%): "
|
|
1234
1233
|
val = _read_input(prompt)
|
|
1235
1234
|
try:
|
|
1236
|
-
|
|
1237
|
-
robot.gripper.
|
|
1238
|
-
messages.append(f"Gripper set to {
|
|
1235
|
+
percent = float(val) # type: ignore
|
|
1236
|
+
robot.gripper.set_position_percent(int(percent)) # type: ignore
|
|
1237
|
+
messages.append(f"Gripper set to {int(percent)}%")
|
|
1239
1238
|
except (ValueError, TypeError):
|
|
1240
1239
|
messages.append("Invalid position value")
|
|
1241
1240
|
except Exception as e:
|
|
@@ -1369,6 +1368,9 @@ def teach_command(args) -> None:
|
|
|
1369
1368
|
# "none" is an explicit CLI override — don't fall through to config
|
|
1370
1369
|
if gripper_name and gripper_name.lower() == "none": # type: ignore
|
|
1371
1370
|
gripper_name = None
|
|
1371
|
+
# Normalize: lowercase, replace _ with - (e.g. "2F_140" → "2f-140")
|
|
1372
|
+
if gripper_name and isinstance(gripper_name, str):
|
|
1373
|
+
gripper_name = gripper_name.lower().replace("_", "-")
|
|
1372
1374
|
points_path = args.points or config.get("points_path") or "points.db"
|
|
1373
1375
|
|
|
1374
1376
|
# Resolve gripper constructor params from config.yaml gripper_config section
|
|
@@ -1406,7 +1408,7 @@ def teach_command(args) -> None:
|
|
|
1406
1408
|
|
|
1407
1409
|
if not ip:
|
|
1408
1410
|
print("Error: No robot IP specified.")
|
|
1409
|
-
print(" Usage: urkit
|
|
1411
|
+
print(" Usage: urkit 192.168.1.50")
|
|
1410
1412
|
print(" Or: urkit (uses last-used IP from config)")
|
|
1411
1413
|
print(f" Config: {_resolve_default_config_path()}")
|
|
1412
1414
|
sys.exit(1)
|
|
@@ -1414,7 +1416,7 @@ def teach_command(args) -> None:
|
|
|
1414
1416
|
# Validate IP format before any connection attempt
|
|
1415
1417
|
if not _validate_ip(ip): # type: ignore
|
|
1416
1418
|
print(f"Error: \"{ip}\" is not a valid IPv4 address.")
|
|
1417
|
-
print(" Usage: urkit
|
|
1419
|
+
print(" Usage: urkit 192.168.1.50")
|
|
1418
1420
|
sys.exit(1)
|
|
1419
1421
|
|
|
1420
1422
|
# Quick ping check to verify reachability
|
|
@@ -7,10 +7,10 @@ Usage::
|
|
|
7
7
|
from urkit import URRobot, ROBOTIQ_2F_85, DigitalGripperConfig
|
|
8
8
|
|
|
9
9
|
# Robotiq preset — one arg does everything
|
|
10
|
-
robot = URRobot(ip="
|
|
10
|
+
robot = URRobot(ip="192.168.1.50", gripper=ROBOTIQ_2F_85)
|
|
11
11
|
|
|
12
12
|
# Digital I/O gripper
|
|
13
|
-
robot = URRobot(ip="
|
|
13
|
+
robot = URRobot(ip="192.168.1.50", gripper=DigitalGripperConfig(pin=3))
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
from urkit.gripper.base import Gripper
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Abstract gripper plugin interface.
|
|
2
2
|
|
|
3
3
|
All gripper backends must implement this interface so that
|
|
4
|
-
``robot.gripper.open()``, ``close()``, and ``
|
|
4
|
+
``robot.gripper.open()``, ``close()``, ``set_position_mm()``, and ``set_position_percent()`` work
|
|
5
5
|
uniformly regardless of the underlying hardware.
|
|
6
6
|
"""
|
|
7
7
|
|
|
@@ -13,7 +13,7 @@ from abc import ABC, abstractmethod
|
|
|
13
13
|
class Gripper(ABC):
|
|
14
14
|
"""Abstract base class for gripper backends.
|
|
15
15
|
|
|
16
|
-
Subclasses must implement ``open()``, ``close()``, and ``
|
|
16
|
+
Subclasses must implement ``open()``, ``close()``, ``set_position_mm()``, and ``set_position_percent()``.
|
|
17
17
|
The ``activate()`` method is a no-op by default and should be overridden
|
|
18
18
|
by backends that require explicit activation (e.g., Robotiq). The factory
|
|
19
19
|
method ``Gripper.create()`` returns the appropriate backend.
|
|
@@ -36,15 +36,15 @@ class Gripper(ABC):
|
|
|
36
36
|
"""
|
|
37
37
|
|
|
38
38
|
@abstractmethod
|
|
39
|
-
def
|
|
40
|
-
"""Set the gripper to a specific position.
|
|
39
|
+
def set_position_mm(self, mm: float) -> None:
|
|
40
|
+
"""Set the gripper to a specific position in millimeters.
|
|
41
41
|
|
|
42
|
-
For Robotiq 2F grippers,
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
For Robotiq 2F grippers, mm is 0 (fully closed) to max_mm
|
|
43
|
+
(fully open). Digital grippers don't support this and raise
|
|
44
|
+
GripperError.
|
|
45
45
|
|
|
46
46
|
Args:
|
|
47
|
-
|
|
47
|
+
mm: Opening in millimeters.
|
|
48
48
|
|
|
49
49
|
Raises:
|
|
50
50
|
GripperError: If the operation fails.
|
|
@@ -77,6 +77,21 @@ class Gripper(ABC):
|
|
|
77
77
|
sockets or release resources (e.g., Robotiq grippers).
|
|
78
78
|
"""
|
|
79
79
|
|
|
80
|
+
def set_position_percent(self, percent: int, *, wait: bool = True) -> None:
|
|
81
|
+
"""Set the gripper to a specific percentage opening.
|
|
82
|
+
|
|
83
|
+
0 = fully open, 100 = fully closed.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
percent: Percentage opening (0-100).
|
|
87
|
+
wait: If True, block until the gripper reaches the target
|
|
88
|
+
position (default True).
|
|
89
|
+
|
|
90
|
+
Raises:
|
|
91
|
+
GripperError: If percent is out of range or the operation
|
|
92
|
+
fails.
|
|
93
|
+
"""
|
|
94
|
+
|
|
80
95
|
def get_position_mm(self) -> float | None:
|
|
81
96
|
"""Return the last commanded position in mm, or None if unknown.
|
|
82
97
|
|
|
@@ -98,14 +98,25 @@ class DigitalGripper(Gripper):
|
|
|
98
98
|
"ON" if self._closed_when_high else "OFF",
|
|
99
99
|
)
|
|
100
100
|
|
|
101
|
-
def
|
|
101
|
+
def set_position_mm(self, mm: float) -> None:
|
|
102
102
|
"""Set the gripper position.
|
|
103
103
|
|
|
104
104
|
Raises:
|
|
105
105
|
GripperError: Always — digital grippers don't support position control.
|
|
106
106
|
"""
|
|
107
107
|
raise GripperError(
|
|
108
|
-
"Digital grippers do not support
|
|
108
|
+
"Digital grippers do not support set_position_mm. "
|
|
109
|
+
"Use open() or close() instead."
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def set_position_percent(self, percent: int, *, wait: bool = True) -> None:
|
|
113
|
+
"""Set the gripper position as a percentage.
|
|
114
|
+
|
|
115
|
+
Raises:
|
|
116
|
+
GripperError: Always — digital grippers don't support position control.
|
|
117
|
+
"""
|
|
118
|
+
raise GripperError(
|
|
119
|
+
"Digital grippers do not support set_position_percent. "
|
|
109
120
|
"Use open() or close() instead."
|
|
110
121
|
)
|
|
111
122
|
|
|
@@ -10,12 +10,12 @@ Usage::
|
|
|
10
10
|
from urkit.gripper.presets import ROBOTIQ_2F_85
|
|
11
11
|
|
|
12
12
|
# One arg — specs + backend
|
|
13
|
-
robot = URRobot(ip="
|
|
13
|
+
robot = URRobot(ip="192.168.1.50", gripper=ROBOTIQ_2F_85)
|
|
14
14
|
|
|
15
15
|
# Digital I/O gripper
|
|
16
16
|
from urkit.gripper.presets import DigitalGripperConfig
|
|
17
17
|
|
|
18
|
-
robot = URRobot(ip="
|
|
18
|
+
robot = URRobot(ip="192.168.1.50", gripper=DigitalGripperConfig(pin=3))
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
from __future__ import annotations
|
|
@@ -33,7 +33,7 @@ class GripperPreset:
|
|
|
33
33
|
center_of_gravity: Center of gravity in tool coordinates [x, y, z], meters.
|
|
34
34
|
tcp_offset: TCP offset [x, y, z, rx, ry, rz], meters/radians.
|
|
35
35
|
backend: Gripper backend name (e.g. ``"robotiq"``).
|
|
36
|
-
max_mm: Maximum finger travel in mm (for ``
|
|
36
|
+
max_mm: Maximum finger travel in mm (for ``set_position_mm()``).
|
|
37
37
|
force: Gripper force 0-100 (default 100).
|
|
38
38
|
speed: Gripper speed 0-100 (default 100).
|
|
39
39
|
"""
|
|
@@ -94,7 +94,7 @@ class RobotiqGripper(Gripper):
|
|
|
94
94
|
if not self._activated:
|
|
95
95
|
raise GripperError(
|
|
96
96
|
"Gripper is not activated. Call activate() before "
|
|
97
|
-
"using open(), close(), or
|
|
97
|
+
"using open(), close(), or set_position_mm()."
|
|
98
98
|
)
|
|
99
99
|
|
|
100
100
|
def _build_script(self, function_call: str) -> str:
|
|
@@ -259,7 +259,7 @@ class RobotiqGripper(Gripper):
|
|
|
259
259
|
self._last_position_mm = 0.0
|
|
260
260
|
logger.debug("Robotiq gripper closed (wait=%s)", wait)
|
|
261
261
|
|
|
262
|
-
def
|
|
262
|
+
def set_position_mm(self, mm: float, *, wait: bool = True) -> None:
|
|
263
263
|
"""Set the gripper to a specific opening in millimeters.
|
|
264
264
|
|
|
265
265
|
Args:
|
|
@@ -285,6 +285,41 @@ class RobotiqGripper(Gripper):
|
|
|
285
285
|
self._last_position_mm = mm
|
|
286
286
|
logger.debug("Robotiq gripper set to %.1f mm (wait=%s)", mm, wait)
|
|
287
287
|
|
|
288
|
+
def set_position_percent(self, percent: int, *, wait: bool = True) -> None:
|
|
289
|
+
"""Set the gripper to a specific percentage opening.
|
|
290
|
+
|
|
291
|
+
0 = fully open, 100 = fully closed. Delegates to the preamble's
|
|
292
|
+
normalized functions (``rq_move_norm`` / ``rq_move_and_wait_norm``)
|
|
293
|
+
which handle the 0-100 → 0-255 gripper protocol conversion.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
percent: Percentage opening (0-100).
|
|
297
|
+
wait: If True, block until the gripper reaches the target
|
|
298
|
+
position (default True).
|
|
299
|
+
|
|
300
|
+
Raises:
|
|
301
|
+
GripperError: If percent is out of range or the gripper
|
|
302
|
+
has not been activated.
|
|
303
|
+
"""
|
|
304
|
+
if not 0 <= percent <= 100:
|
|
305
|
+
raise GripperError(
|
|
306
|
+
f"Robotiq gripper percent must be 0-100, got {percent}."
|
|
307
|
+
)
|
|
308
|
+
self._require_activated()
|
|
309
|
+
func = (
|
|
310
|
+
f"rq_move_and_wait_norm({percent})"
|
|
311
|
+
if wait
|
|
312
|
+
else f"rq_move_norm({percent})"
|
|
313
|
+
)
|
|
314
|
+
self._send_script(self._build_script(func))
|
|
315
|
+
# Track last position in mm for consistency with get_position_mm()
|
|
316
|
+
mm = self._max_mm * (1 - percent / 100.0)
|
|
317
|
+
self._last_position_mm = mm
|
|
318
|
+
logger.debug(
|
|
319
|
+
"Robotiq gripper set to %d%% (%.1f mm) (wait=%s)",
|
|
320
|
+
percent, mm, wait,
|
|
321
|
+
)
|
|
322
|
+
|
|
288
323
|
def set_force(self, force: int) -> None:
|
|
289
324
|
"""Set gripper force for subsequent movements.
|
|
290
325
|
|
|
@@ -87,7 +87,7 @@ class URRobot:
|
|
|
87
87
|
|
|
88
88
|
Example:
|
|
89
89
|
>>> from urkit import URRobot, ROBOTIQ_2F_85
|
|
90
|
-
>>> robot = URRobot(ip="
|
|
90
|
+
>>> robot = URRobot(ip="192.168.1.50", gripper=ROBOTIQ_2F_85)
|
|
91
91
|
>>> robot.move_relative([0.01, 0, 0, 0, 0, 0]) # works without points
|
|
92
92
|
>>> robot.points_db = "points.db" # set lazily
|
|
93
93
|
>>> robot.save_point("home")
|
|
@@ -118,6 +118,7 @@ class URRobot:
|
|
|
118
118
|
resolved_cog: list[float] = [0.0, 0.0, 0.0]
|
|
119
119
|
resolved_tcp: list[float] | None = None
|
|
120
120
|
gripper_backend: str | None = None
|
|
121
|
+
self._payload: float = 0.0
|
|
121
122
|
if isinstance(gripper, GripperPreset):
|
|
122
123
|
resolved_payload = gripper.mass
|
|
123
124
|
resolved_cog = list(gripper.center_of_gravity)
|
|
@@ -246,6 +247,10 @@ class URRobot:
|
|
|
246
247
|
self.set_tcp_offset(resolved_tcp)
|
|
247
248
|
if resolved_payload > 0:
|
|
248
249
|
self.set_payload(resolved_payload, resolved_cog)
|
|
250
|
+
else:
|
|
251
|
+
# No payload configured — explicitly zero it on the robot
|
|
252
|
+
# to clear any stale value from a previous session.
|
|
253
|
+
self.set_payload(0.0)
|
|
249
254
|
|
|
250
255
|
logger.info("URRobot initialized at %s", ip)
|
|
251
256
|
|
|
@@ -322,7 +327,7 @@ class URRobot:
|
|
|
322
327
|
|
|
323
328
|
Config file keys::
|
|
324
329
|
|
|
325
|
-
robot_ip:
|
|
330
|
+
robot_ip: 192.168.1.50
|
|
326
331
|
points_path: points.db
|
|
327
332
|
gripper: hand-e
|
|
328
333
|
gripper_config:
|
|
@@ -335,7 +340,7 @@ class URRobot:
|
|
|
335
340
|
Example:
|
|
336
341
|
>>> robot = URRobot.from_config("config.yaml")
|
|
337
342
|
>>> robot = URRobot.from_config("config.yaml", ip="10.0.0.50")
|
|
338
|
-
>>> robot = URRobot.from_config({"robot_ip": "
|
|
343
|
+
>>> robot = URRobot.from_config({"robot_ip": "192.168.1.50", "points_path": "points.db", "gripper": "2f-85"})
|
|
339
344
|
"""
|
|
340
345
|
from urkit.config import resolve_config
|
|
341
346
|
|
|
@@ -740,9 +745,25 @@ class URRobot:
|
|
|
740
745
|
cog = center_of_gravity if center_of_gravity is not None else [0.0, 0.0, 0.0]
|
|
741
746
|
if len(cog) != 3:
|
|
742
747
|
raise MotionError(f"Center of gravity must have 3 values, got {len(cog)}.")
|
|
748
|
+
# Try newer setTargetPayload first (PolyScope 5.11.0+), fall back to setPayload
|
|
743
749
|
try:
|
|
744
|
-
|
|
745
|
-
|
|
750
|
+
inertia = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
|
751
|
+
self._rtde_c.setTargetPayload(mass, cog, inertia)
|
|
752
|
+
self._payload = mass
|
|
753
|
+
logger.info("Payload set to %.2f kg, CoG=%s (setTargetPayload)", mass, cog)
|
|
754
|
+
except AttributeError:
|
|
755
|
+
# setTargetPayload not available — older PolyScope
|
|
756
|
+
try:
|
|
757
|
+
self._rtde_c.setPayload(mass, cog)
|
|
758
|
+
self._payload = mass
|
|
759
|
+
logger.warning(
|
|
760
|
+
"Payload set to %.2f kg, CoG=%s (setPayload fallback — "
|
|
761
|
+
"setTargetPayload unavailable, likely PolyScope < 5.11.0)",
|
|
762
|
+
mass,
|
|
763
|
+
cog,
|
|
764
|
+
)
|
|
765
|
+
except Exception as e2:
|
|
766
|
+
raise MotionError(f"Failed to set payload: {e2}")
|
|
746
767
|
except Exception as e:
|
|
747
768
|
raise MotionError(f"Failed to set payload: {e}")
|
|
748
769
|
|
|
@@ -894,6 +915,8 @@ class URRobot:
|
|
|
894
915
|
|
|
895
916
|
try:
|
|
896
917
|
if linear:
|
|
918
|
+
if not self._rtde_c.getInverseKinematicsHasSolution(pose):
|
|
919
|
+
raise MotionError(f"Pose unreachable: {pose}")
|
|
897
920
|
self._motion.movel(pose, vel=vel, acc=acc, asynchronous=asynchronous)
|
|
898
921
|
else:
|
|
899
922
|
joints = self.inverse_kinematics(pose)
|
|
@@ -1262,7 +1285,22 @@ class URRobot:
|
|
|
1262
1285
|
|
|
1263
1286
|
def get_payload(self) -> float:
|
|
1264
1287
|
"""Get the currently configured payload mass (kg)."""
|
|
1265
|
-
return self.
|
|
1288
|
+
return self._payload
|
|
1289
|
+
|
|
1290
|
+
def get_polyscope_version(self) -> str | None:
|
|
1291
|
+
"""Get the PolyScope version string via Dashboard (e.g. '5.25.0').
|
|
1292
|
+
|
|
1293
|
+
Returns:
|
|
1294
|
+
Version string, or None if Dashboard is unavailable.
|
|
1295
|
+
"""
|
|
1296
|
+
try:
|
|
1297
|
+
if self._dashboard is None:
|
|
1298
|
+
self._connect_dashboard()
|
|
1299
|
+
if self._dashboard is None:
|
|
1300
|
+
return None
|
|
1301
|
+
return _dashboard_command(self._dashboard, "version") # type: ignore
|
|
1302
|
+
except Exception:
|
|
1303
|
+
return None
|
|
1266
1304
|
|
|
1267
1305
|
def is_protective_stopped(self) -> bool:
|
|
1268
1306
|
"""Check if the robot is in protective stop."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: urkit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: Universal Robots e-Series control toolkit built on ur_rtde
|
|
5
5
|
Author: URKit Contributors
|
|
6
6
|
License: MIT
|
|
@@ -69,9 +69,9 @@ Requires Python 3.8+ and a Universal Robots e-Series (UR3e to UR30).
|
|
|
69
69
|
### Robot Setup (one-time)
|
|
70
70
|
|
|
71
71
|
1. **Network**: `☰` → `System` → `System` → `Network`. Set a static IP on the robot and a matching one on your PC. Both addresses must share the same first three octets (the network), with a different last octet (the host). For example:
|
|
72
|
-
- **Robot**: `
|
|
73
|
-
- **PC**: `
|
|
74
|
-
- Verify with `ping
|
|
72
|
+
- **Robot**: `192.168.1.50` / Subnet `255.255.255.0`
|
|
73
|
+
- **PC**: `192.168.1.1` / Netmask `255.255.255.0`
|
|
74
|
+
- Verify with `ping 192.168.1.50`. Connect via direct Ethernet cable or a switch.
|
|
75
75
|
2. **Remote Control**: `☰` → `System` → `Remote Control`: Enable. Press the remote/local button on the pendant.
|
|
76
76
|
3. **Security**: `☰` → `Security` → `Services`: enable RTDE and disable EtherNet/IP, PROFINET, or MODBUS if they're claiming RTDE registers. Save and restart.
|
|
77
77
|
|
|
@@ -84,7 +84,7 @@ If you have a Robotiq gripper, install the **Robotiq Gripper Control** URCap fir
|
|
|
84
84
|
```python
|
|
85
85
|
from urkit import URRobot, ROBOTIQ_HAND_E # or ROBOTIQ_2F_85, ROBOTIQ_2F_140, or gripper=None
|
|
86
86
|
|
|
87
|
-
robot = URRobot(ip="
|
|
87
|
+
robot = URRobot(ip="192.168.1.50", points="points.db", gripper=ROBOTIQ_HAND_E)
|
|
88
88
|
robot.gripper.activate()
|
|
89
89
|
|
|
90
90
|
robot.move_to("home")
|
|
@@ -111,7 +111,7 @@ URKit provides two CLI tools: **teach** for interactive robot control, and **poi
|
|
|
111
111
|
The interactive teach pendant for moving the robot, saving points, and checking telemetry:
|
|
112
112
|
|
|
113
113
|
```bash
|
|
114
|
-
urkit teach
|
|
114
|
+
urkit teach 192.168.1.50 # with robot IP
|
|
115
115
|
urkit teach # reads IP from config.yaml
|
|
116
116
|
```
|
|
117
117
|
|
|
@@ -192,7 +192,7 @@ All movement and orientation keys support **hold-to-repeat**.
|
|
|
192
192
|
<tr><td><code>V</code></td><td>Set position (mm)</td></tr>
|
|
193
193
|
<tr><td><code>6</code></td><td>Set speed (0-100)</td></tr>
|
|
194
194
|
<tr><td><code>7</code></td><td>Set force (0-100)</td></tr>
|
|
195
|
-
<tr><td colspan="3">Gripper line shows: `Connected
|
|
195
|
+
<tr><td colspan="3">Gripper line shows: `Connected 25.0mm (50%) F=100 S=100`</td></tr>
|
|
196
196
|
</table>
|
|
197
197
|
</td>
|
|
198
198
|
<td align="center" style="width:33%">
|
|
@@ -257,7 +257,7 @@ Delta movements (W/S/A/D/Q/E) use step-size-based velocities that scale with the
|
|
|
257
257
|
To disable the slow default and use full speed, pass `--expert` or set `expert_mode: true` in your config:
|
|
258
258
|
|
|
259
259
|
```bash
|
|
260
|
-
urkit teach
|
|
260
|
+
urkit teach 192.168.1.50 --expert
|
|
261
261
|
```
|
|
262
262
|
|
|
263
263
|
```yaml
|
|
@@ -274,14 +274,14 @@ expert_mode: true
|
|
|
274
274
|
```python
|
|
275
275
|
from urkit import URRobot, ROBOTIQ_HAND_E
|
|
276
276
|
|
|
277
|
-
robot = URRobot(ip="
|
|
277
|
+
robot = URRobot(ip="192.168.1.50", points="points.db", gripper=ROBOTIQ_HAND_E)
|
|
278
278
|
```
|
|
279
279
|
|
|
280
280
|
With custom motion defaults:
|
|
281
281
|
|
|
282
282
|
```python
|
|
283
283
|
robot = URRobot(
|
|
284
|
-
ip="
|
|
284
|
+
ip="192.168.1.50",
|
|
285
285
|
points="points.db",
|
|
286
286
|
gripper=ROBOTIQ_HAND_E,
|
|
287
287
|
default_vel=0.5, # m/s
|
|
@@ -313,7 +313,8 @@ robot.gripper.is_activated() # check activation state
|
|
|
313
313
|
robot.gripper.open() # fully open (blocking by default)
|
|
314
314
|
robot.gripper.close() # fully closed, stops on contact
|
|
315
315
|
robot.gripper.open(wait=False) # non-blocking return
|
|
316
|
-
robot.gripper.
|
|
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)
|
|
317
318
|
robot.gripper.set_force(50) # grip force: 0-100 (Robotiq only)
|
|
318
319
|
robot.gripper.set_speed(80) # movement speed: 0-100 (Robotiq only)
|
|
319
320
|
```
|
|
@@ -321,7 +322,7 @@ robot.gripper.set_speed(80) # movement speed: 0-100 (Robotiq only)
|
|
|
321
322
|
Override preset values for custom fingers:
|
|
322
323
|
|
|
323
324
|
```python
|
|
324
|
-
robot = URRobot(ip="
|
|
325
|
+
robot = URRobot(ip="192.168.1.50", points="points.db", gripper=ROBOTIQ_HAND_E, max_mm=120)
|
|
325
326
|
```
|
|
326
327
|
|
|
327
328
|
#### Digital I/O Grippers
|
|
@@ -332,7 +333,7 @@ Robotiq grippers use a serial protocol over the robot's RS485 port. If you have
|
|
|
332
333
|
from urkit import URRobot, DigitalGripperConfig
|
|
333
334
|
|
|
334
335
|
robot = URRobot(
|
|
335
|
-
ip="
|
|
336
|
+
ip="192.168.1.50",
|
|
336
337
|
points="points.db",
|
|
337
338
|
gripper=DigitalGripperConfig(pin=3), # pin 3 goes high = close
|
|
338
339
|
)
|
|
@@ -348,7 +349,7 @@ robot.gripper.close() # turn pin 3 on
|
|
|
348
349
|
The points database is optional. Create a robot without one and attach later:
|
|
349
350
|
|
|
350
351
|
```python
|
|
351
|
-
robot = URRobot(ip="
|
|
352
|
+
robot = URRobot(ip="192.168.1.50")
|
|
352
353
|
robot.points_db = "points.db"
|
|
353
354
|
```
|
|
354
355
|
|
|
@@ -552,7 +553,7 @@ URKit searches for `config.yaml` in this order:
|
|
|
552
553
|
|
|
553
554
|
| Key | Description | Example |
|
|
554
555
|
|-----|-------------|---------|
|
|
555
|
-
| `robot_ip` | Robot IP address | `
|
|
556
|
+
| `robot_ip` | Robot IP address | `192.168.1.50` |
|
|
556
557
|
| `points_path` | Path to SQLite points database | `points.db` |
|
|
557
558
|
| `gripper` | Gripper preset name | `hand-e`, `2f-85`, `2f-140`, `digital` |
|
|
558
559
|
| `default_vel` | Default linear velocity (m/s) | `0.5` |
|
|
@@ -577,7 +578,7 @@ gripper_config:
|
|
|
577
578
|
|
|
578
579
|
### CLI Override Precedence
|
|
579
580
|
|
|
580
|
-
1. **CLI flags.** `urkit teach
|
|
581
|
+
1. **CLI flags.** `urkit teach 192.168.1.50 --gripper none`
|
|
581
582
|
2. **Config file.** Values from `config.yaml`
|
|
582
583
|
3. **Built-in defaults.** `points.db`, no gripper, 0.5 m/s velocity
|
|
583
584
|
|
|
@@ -586,7 +587,7 @@ gripper_config:
|
|
|
586
587
|
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.
|
|
587
588
|
|
|
588
589
|
```bash
|
|
589
|
-
urkit teach
|
|
590
|
+
urkit teach 192.168.1.50 --gripper hand-e # test, then press Y
|
|
590
591
|
urkit teach # next time: reads from config
|
|
591
592
|
```
|
|
592
593
|
|
|
@@ -605,7 +606,7 @@ from urkit import load_config, resolve_config
|
|
|
605
606
|
config = load_config() # auto-resolve
|
|
606
607
|
config = load_config("/path/to/my.yaml") # explicit path
|
|
607
608
|
path = resolve_config() # returns Path or None
|
|
608
|
-
robot = URRobot.from_config({"robot_ip": "
|
|
609
|
+
robot = URRobot.from_config({"robot_ip": "192.168.1.50", "gripper": "2f-85"})
|
|
609
610
|
```
|
|
610
611
|
|
|
611
612
|
---
|
|
@@ -639,7 +640,7 @@ robot.reconnect_rtde() # reconnect after a drop
|
|
|
639
640
|
from urkit import URKitError, MotionError, PointError
|
|
640
641
|
|
|
641
642
|
try:
|
|
642
|
-
robot = URRobot(ip="
|
|
643
|
+
robot = URRobot(ip="192.168.1.50", points="points.db")
|
|
643
644
|
except RobotNotInRemoteModeError:
|
|
644
645
|
print("Enable remote control on the teach pendant!")
|
|
645
646
|
except RtdeRegisterConflictError:
|
|
@@ -49,10 +49,10 @@ class TestRobotiqActivation:
|
|
|
49
49
|
with pytest.raises(GripperError, match="not activated"):
|
|
50
50
|
self.gripper.close()
|
|
51
51
|
|
|
52
|
-
def
|
|
53
|
-
"""
|
|
52
|
+
def test_set_position_mm_without_activate_raises(self):
|
|
53
|
+
"""set_position_mm() raises if gripper is not activated."""
|
|
54
54
|
with pytest.raises(GripperError, match="not activated"):
|
|
55
|
-
self.gripper.
|
|
55
|
+
self.gripper.set_position_mm(50)
|
|
56
56
|
|
|
57
57
|
def test_activate_sends_script_and_sets_flag(self):
|
|
58
58
|
"""activate() sends the preamble+activation script and sets _activated."""
|
|
@@ -86,11 +86,11 @@ class TestRobotiqActivation:
|
|
|
86
86
|
# activate (combined check+activate+open) + close = 2 calls
|
|
87
87
|
assert self.rtde.sendCustomScriptFunction.call_count == 2
|
|
88
88
|
|
|
89
|
-
def
|
|
90
|
-
"""
|
|
89
|
+
def test_set_position_mm_after_activate(self):
|
|
90
|
+
"""set_position_mm() works after activate()."""
|
|
91
91
|
self.gripper.activate()
|
|
92
|
-
self.gripper.
|
|
93
|
-
# activate (combined check+activate+open) +
|
|
92
|
+
self.gripper.set_position_mm(25)
|
|
93
|
+
# activate (combined check+activate+open) + set_position_mm = 2 calls
|
|
94
94
|
assert self.rtde.sendCustomScriptFunction.call_count == 2
|
|
95
95
|
|
|
96
96
|
def test_requires_rtde_control(self):
|
|
@@ -137,16 +137,16 @@ class TestRobotiqActivation:
|
|
|
137
137
|
self.gripper.close()
|
|
138
138
|
assert self.gripper.get_position_mm() == 0.0
|
|
139
139
|
|
|
140
|
-
def
|
|
140
|
+
def test_get_position_mm_after_set_position_mm(self):
|
|
141
141
|
"""get_position_mm() returns the commanded position."""
|
|
142
142
|
self.gripper.activate()
|
|
143
|
-
self.gripper.
|
|
143
|
+
self.gripper.set_position_mm(25)
|
|
144
144
|
assert self.gripper.get_position_mm() == 25.0
|
|
145
145
|
|
|
146
146
|
def test_get_position_mm_after_disconnect(self):
|
|
147
147
|
"""get_position_mm() returns None after disconnect."""
|
|
148
148
|
self.gripper.activate()
|
|
149
|
-
self.gripper.
|
|
149
|
+
self.gripper.set_position_mm(30)
|
|
150
150
|
self.gripper.disconnect()
|
|
151
151
|
assert self.gripper.get_position_mm() is None
|
|
152
152
|
|
|
@@ -189,10 +189,10 @@ class TestDigitalActivation:
|
|
|
189
189
|
gripper.close()
|
|
190
190
|
mock_rtde.setStandardDigitalOut.assert_called_with(1, False)
|
|
191
191
|
|
|
192
|
-
def
|
|
192
|
+
def test_set_position_mm_raises(self, mock_rtde, mock_rtde_r):
|
|
193
193
|
gripper = DigitalGripper(mock_rtde, mock_rtde_r, pin=0)
|
|
194
|
-
with pytest.raises(GripperError, match="do not support
|
|
195
|
-
gripper.
|
|
194
|
+
with pytest.raises(GripperError, match="do not support set_position_mm"):
|
|
195
|
+
gripper.set_position_mm(0)
|
|
196
196
|
|
|
197
197
|
def test_set_force_raises(self, mock_rtde, mock_rtde_r):
|
|
198
198
|
gripper = DigitalGripper(mock_rtde, mock_rtde_r, pin=0)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Integration tests against a real Universal Robot.
|
|
2
2
|
|
|
3
3
|
Set ROBOT_IP environment variable to run:
|
|
4
|
-
ROBOT_IP=
|
|
4
|
+
ROBOT_IP=192.168.1.50 PYTHONPATH= .venv/bin/python -m pytest tests/test_robot_integration.py -v
|
|
5
5
|
|
|
6
6
|
Tests are skipped automatically if ROBOT_IP is not set or the robot
|
|
7
7
|
is unreachable. All tests are designed to be safe — read-only telemetry
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|