urkit 0.4.0__tar.gz → 0.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {urkit-0.4.0 → urkit-0.4.2}/PKG-INFO +76 -11
- {urkit-0.4.0 → urkit-0.4.2}/README.md +75 -10
- {urkit-0.4.0 → urkit-0.4.2}/pyproject.toml +1 -1
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/__init__.py +1 -1
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/__main__.py +22 -1
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/cli/connection_monitor.py +8 -1
- urkit-0.4.2/src/urkit/cli/init.py +83 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/cli/points.py +28 -35
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/cli/teach.py +100 -154
- urkit-0.4.2/src/urkit/cli/terminal.py +233 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/points.py +8 -5
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/robot.py +7 -6
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit.egg-info/PKG-INFO +76 -11
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit.egg-info/SOURCES.txt +2 -0
- {urkit-0.4.0 → urkit-0.4.2}/setup.cfg +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/cli/__init__.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/cli/colors.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/config.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/connection.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/exceptions.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/geometry.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/gripper/__init__.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/gripper/base.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/gripper/digital.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/gripper/presets.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/gripper/robotiq.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/gripper/robotiq_preamble.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/io.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/motion.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit/telemetry.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit.egg-info/dependency_links.txt +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit.egg-info/entry_points.txt +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit.egg-info/requires.txt +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/src/urkit.egg-info/top_level.txt +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/tests/test_exceptions.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/tests/test_geometry.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/tests/test_gripper.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/tests/test_gripper_factory.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/tests/test_gripper_presets.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/tests/test_move_sequence.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/tests/test_points.py +0 -0
- {urkit-0.4.0 → urkit-0.4.2}/tests/test_robot_integration.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: urkit
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Universal Robots e-Series control toolkit built on ur_rtde
|
|
5
5
|
Author: URKit Contributors
|
|
6
6
|
License: MIT
|
|
@@ -56,12 +56,14 @@ The typical workflow: teach points with the pendant, write a few lines of Python
|
|
|
56
56
|
|
|
57
57
|
- [Quick Start](#quick-start)
|
|
58
58
|
- [Configuration](#configuration)
|
|
59
|
+
- [Create a Config File](#create-a-config-file)
|
|
59
60
|
- [Location](#location)
|
|
60
61
|
- [Keys](#keys)
|
|
61
62
|
- [Gripper Config](#gripper-config)
|
|
62
63
|
- [Saving Config](#saving-config)
|
|
63
64
|
- [Programmatic](#programmatic)
|
|
64
65
|
- [Interactive CLI](#interactive-cli)
|
|
66
|
+
- [Init](#init)
|
|
65
67
|
- [Teach Mode](#teach-mode)
|
|
66
68
|
- [Points Explorer](#points-explorer)
|
|
67
69
|
- [Key Map](#key-map)
|
|
@@ -92,12 +94,40 @@ The `-U` (upgrade) flag ensures you always get the latest version. This project
|
|
|
92
94
|
|
|
93
95
|
Requires Python 3.8+ and a Universal Robots e-Series (UR3e to UR30).
|
|
94
96
|
|
|
97
|
+
### Platform Support
|
|
98
|
+
|
|
99
|
+
- **Linux, macOS**: fully supported
|
|
100
|
+
- **Windows**: the Python library works on Windows. The interactive CLIs (`urkit teach`, `urkit points`) run on Windows but are **not fully tested and not officially supported**. A warning is printed at startup. If you hit input or console problems, use Linux, macOS, or WSL2
|
|
101
|
+
|
|
95
102
|
### Robot Setup (one-time)
|
|
96
103
|
|
|
97
|
-
1. **Network**:
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
104
|
+
1. **Network**: Connect your PC to the robot via Ethernet cable (direct) or a switch.
|
|
105
|
+
|
|
106
|
+
First, configure the robot: `☰` → `System` → `System` → `Network`. Set a static IP, e.g., `192.168.1.50` / Subnet `255.255.255.0`.
|
|
107
|
+
|
|
108
|
+
Then set a matching static IP on your PC. The exact IP can be anything, as long as the first three octets match the robot's network and the last octet is different:
|
|
109
|
+
|
|
110
|
+
**Linux**:
|
|
111
|
+
- Open `Settings` → `Network` → click the gear icon next to `Wired`
|
|
112
|
+
- Go to `IPv4` tab → change `Automatic (DHCP)` to `Manual`
|
|
113
|
+
- Enter Address: `192.168.1.1`, Netmask: `255.255.255.0`
|
|
114
|
+
- Click `Apply`, then toggle the connection off and on
|
|
115
|
+
|
|
116
|
+
**macOS**:
|
|
117
|
+
- Open `System Settings` → `Network` → click `Ethernet` (or your adapter)
|
|
118
|
+
- Click `Details...` next to the adapter name → `TCP/IP`
|
|
119
|
+
- Set `Configure IPv4` to `Manually`
|
|
120
|
+
- Enter IP: `192.168.1.1`, Subnet Mask: `255.255.255.0`
|
|
121
|
+
- Click `OK`
|
|
122
|
+
|
|
123
|
+
**Windows**:
|
|
124
|
+
- Open `Settings` → `Network & internet` → `Ethernet`
|
|
125
|
+
- Click the adapter name (e.g., `Ethernet`) at the top
|
|
126
|
+
- Under `IP settings`, click `Edit` → select `Manual`
|
|
127
|
+
- Toggle `IPv4` On, enter IP: `192.168.1.1`, Subnet mask: `255.255.255.0`
|
|
128
|
+
- Click `Save`
|
|
129
|
+
|
|
130
|
+
Verify connectivity: `ping 192.168.1.50`.
|
|
101
131
|
2. **Remote Control**: `☰` → `System` → `Remote Control`: Enable. Press the remote/local button on the pendant.
|
|
102
132
|
3. **Security**: `☰` → `Security` → `Services`: enable RTDE and disable EtherNet/IP, PROFINET, or MODBUS if they're claiming RTDE registers. Save and restart.
|
|
103
133
|
|
|
@@ -122,9 +152,20 @@ robot.gripper.open()
|
|
|
122
152
|
|
|
123
153
|
The typical workflow:
|
|
124
154
|
|
|
125
|
-
1. **
|
|
126
|
-
2. **
|
|
127
|
-
3. **
|
|
155
|
+
1. **Scaffold config.** Run `urkit init` to create a default `config.yaml`.
|
|
156
|
+
2. **Edit it.** Set your robot IP, gripper type, and other settings.
|
|
157
|
+
3. **Teach points.** Run `urkit teach` (auto-loads config) to position the robot and save waypoints.
|
|
158
|
+
4. **Write code.** Load the config and move to points by name:
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
from urkit import URRobot
|
|
162
|
+
|
|
163
|
+
robot = URRobot.from_config("config.yaml")
|
|
164
|
+
robot.move_to("home")
|
|
165
|
+
robot.move_to("pick", offset_z=0.05)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
5. **Iterate.** Add more points, tweak your code, repeat.
|
|
128
169
|
|
|
129
170
|
---
|
|
130
171
|
|
|
@@ -132,9 +173,21 @@ The typical workflow:
|
|
|
132
173
|
|
|
133
174
|
URKit uses a YAML config file (`config.yaml`) to persist settings between sessions.
|
|
134
175
|
|
|
176
|
+
### Create a Config File
|
|
177
|
+
|
|
178
|
+
Generate a default config with all options documented:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
urkit init # writes config.yaml in CWD
|
|
182
|
+
urkit init --output station_a.yaml # custom path
|
|
183
|
+
urkit init --force # overwrite if exists
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Edit the generated file with your robot IP and gripper settings.
|
|
187
|
+
|
|
135
188
|
### Location
|
|
136
189
|
|
|
137
|
-
|
|
190
|
+
The CLI auto-loads `config.yaml` from the current working directory. Pass `--config` for a custom path.
|
|
138
191
|
|
|
139
192
|
### Keys
|
|
140
193
|
|
|
@@ -224,7 +277,19 @@ robot = URRobot.from_config({"robot_ip": "192.168.1.50", "gripper": "2f-85"}) #
|
|
|
224
277
|
|
|
225
278
|
## Interactive CLI
|
|
226
279
|
|
|
227
|
-
URKit provides
|
|
280
|
+
URKit provides three CLI tools: **init** to scaffold a config file, **teach** for interactive robot control, and **points** for browsing saved waypoints.
|
|
281
|
+
|
|
282
|
+
### Init
|
|
283
|
+
|
|
284
|
+
Generate a default config file with all options documented:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
urkit init # writes config.yaml in CWD
|
|
288
|
+
urkit init --output station_a.yaml # custom path
|
|
289
|
+
urkit init --force # overwrite if exists
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Teach Mode
|
|
228
293
|
|
|
229
294
|
### Teach Mode
|
|
230
295
|
|
|
@@ -603,7 +668,7 @@ Default is `None` (controller handles IK natively). Set it globally when most of
|
|
|
603
668
|
|
|
604
669
|
#### Point Management
|
|
605
670
|
|
|
606
|
-
Points are stored in the
|
|
671
|
+
Points are stored as absolute TCP poses in the robot base frame. When you move to a point, the controller moves whatever TCP is currently active to that base-frame pose, so the tool tip lands at the same physical location. Change the TCP offset (or swap grippers) and your saved points stay valid.
|
|
607
672
|
|
|
608
673
|
```python
|
|
609
674
|
robot.save_point("here")
|
|
@@ -30,12 +30,14 @@ The typical workflow: teach points with the pendant, write a few lines of Python
|
|
|
30
30
|
|
|
31
31
|
- [Quick Start](#quick-start)
|
|
32
32
|
- [Configuration](#configuration)
|
|
33
|
+
- [Create a Config File](#create-a-config-file)
|
|
33
34
|
- [Location](#location)
|
|
34
35
|
- [Keys](#keys)
|
|
35
36
|
- [Gripper Config](#gripper-config)
|
|
36
37
|
- [Saving Config](#saving-config)
|
|
37
38
|
- [Programmatic](#programmatic)
|
|
38
39
|
- [Interactive CLI](#interactive-cli)
|
|
40
|
+
- [Init](#init)
|
|
39
41
|
- [Teach Mode](#teach-mode)
|
|
40
42
|
- [Points Explorer](#points-explorer)
|
|
41
43
|
- [Key Map](#key-map)
|
|
@@ -66,12 +68,40 @@ The `-U` (upgrade) flag ensures you always get the latest version. This project
|
|
|
66
68
|
|
|
67
69
|
Requires Python 3.8+ and a Universal Robots e-Series (UR3e to UR30).
|
|
68
70
|
|
|
71
|
+
### Platform Support
|
|
72
|
+
|
|
73
|
+
- **Linux, macOS**: fully supported
|
|
74
|
+
- **Windows**: the Python library works on Windows. The interactive CLIs (`urkit teach`, `urkit points`) run on Windows but are **not fully tested and not officially supported**. A warning is printed at startup. If you hit input or console problems, use Linux, macOS, or WSL2
|
|
75
|
+
|
|
69
76
|
### Robot Setup (one-time)
|
|
70
77
|
|
|
71
|
-
1. **Network**:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
1. **Network**: Connect your PC to the robot via Ethernet cable (direct) or a switch.
|
|
79
|
+
|
|
80
|
+
First, configure the robot: `☰` → `System` → `System` → `Network`. Set a static IP, e.g., `192.168.1.50` / Subnet `255.255.255.0`.
|
|
81
|
+
|
|
82
|
+
Then set a matching static IP on your PC. The exact IP can be anything, as long as the first three octets match the robot's network and the last octet is different:
|
|
83
|
+
|
|
84
|
+
**Linux**:
|
|
85
|
+
- Open `Settings` → `Network` → click the gear icon next to `Wired`
|
|
86
|
+
- Go to `IPv4` tab → change `Automatic (DHCP)` to `Manual`
|
|
87
|
+
- Enter Address: `192.168.1.1`, Netmask: `255.255.255.0`
|
|
88
|
+
- Click `Apply`, then toggle the connection off and on
|
|
89
|
+
|
|
90
|
+
**macOS**:
|
|
91
|
+
- Open `System Settings` → `Network` → click `Ethernet` (or your adapter)
|
|
92
|
+
- Click `Details...` next to the adapter name → `TCP/IP`
|
|
93
|
+
- Set `Configure IPv4` to `Manually`
|
|
94
|
+
- Enter IP: `192.168.1.1`, Subnet Mask: `255.255.255.0`
|
|
95
|
+
- Click `OK`
|
|
96
|
+
|
|
97
|
+
**Windows**:
|
|
98
|
+
- Open `Settings` → `Network & internet` → `Ethernet`
|
|
99
|
+
- Click the adapter name (e.g., `Ethernet`) at the top
|
|
100
|
+
- Under `IP settings`, click `Edit` → select `Manual`
|
|
101
|
+
- Toggle `IPv4` On, enter IP: `192.168.1.1`, Subnet mask: `255.255.255.0`
|
|
102
|
+
- Click `Save`
|
|
103
|
+
|
|
104
|
+
Verify connectivity: `ping 192.168.1.50`.
|
|
75
105
|
2. **Remote Control**: `☰` → `System` → `Remote Control`: Enable. Press the remote/local button on the pendant.
|
|
76
106
|
3. **Security**: `☰` → `Security` → `Services`: enable RTDE and disable EtherNet/IP, PROFINET, or MODBUS if they're claiming RTDE registers. Save and restart.
|
|
77
107
|
|
|
@@ -96,9 +126,20 @@ robot.gripper.open()
|
|
|
96
126
|
|
|
97
127
|
The typical workflow:
|
|
98
128
|
|
|
99
|
-
1. **
|
|
100
|
-
2. **
|
|
101
|
-
3. **
|
|
129
|
+
1. **Scaffold config.** Run `urkit init` to create a default `config.yaml`.
|
|
130
|
+
2. **Edit it.** Set your robot IP, gripper type, and other settings.
|
|
131
|
+
3. **Teach points.** Run `urkit teach` (auto-loads config) to position the robot and save waypoints.
|
|
132
|
+
4. **Write code.** Load the config and move to points by name:
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from urkit import URRobot
|
|
136
|
+
|
|
137
|
+
robot = URRobot.from_config("config.yaml")
|
|
138
|
+
robot.move_to("home")
|
|
139
|
+
robot.move_to("pick", offset_z=0.05)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
5. **Iterate.** Add more points, tweak your code, repeat.
|
|
102
143
|
|
|
103
144
|
---
|
|
104
145
|
|
|
@@ -106,9 +147,21 @@ The typical workflow:
|
|
|
106
147
|
|
|
107
148
|
URKit uses a YAML config file (`config.yaml`) to persist settings between sessions.
|
|
108
149
|
|
|
150
|
+
### Create a Config File
|
|
151
|
+
|
|
152
|
+
Generate a default config with all options documented:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
urkit init # writes config.yaml in CWD
|
|
156
|
+
urkit init --output station_a.yaml # custom path
|
|
157
|
+
urkit init --force # overwrite if exists
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Edit the generated file with your robot IP and gripper settings.
|
|
161
|
+
|
|
109
162
|
### Location
|
|
110
163
|
|
|
111
|
-
|
|
164
|
+
The CLI auto-loads `config.yaml` from the current working directory. Pass `--config` for a custom path.
|
|
112
165
|
|
|
113
166
|
### Keys
|
|
114
167
|
|
|
@@ -198,7 +251,19 @@ robot = URRobot.from_config({"robot_ip": "192.168.1.50", "gripper": "2f-85"}) #
|
|
|
198
251
|
|
|
199
252
|
## Interactive CLI
|
|
200
253
|
|
|
201
|
-
URKit provides
|
|
254
|
+
URKit provides three CLI tools: **init** to scaffold a config file, **teach** for interactive robot control, and **points** for browsing saved waypoints.
|
|
255
|
+
|
|
256
|
+
### Init
|
|
257
|
+
|
|
258
|
+
Generate a default config file with all options documented:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
urkit init # writes config.yaml in CWD
|
|
262
|
+
urkit init --output station_a.yaml # custom path
|
|
263
|
+
urkit init --force # overwrite if exists
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Teach Mode
|
|
202
267
|
|
|
203
268
|
### Teach Mode
|
|
204
269
|
|
|
@@ -577,7 +642,7 @@ Default is `None` (controller handles IK natively). Set it globally when most of
|
|
|
577
642
|
|
|
578
643
|
#### Point Management
|
|
579
644
|
|
|
580
|
-
Points are stored in the
|
|
645
|
+
Points are stored as absolute TCP poses in the robot base frame. When you move to a point, the controller moves whatever TCP is currently active to that base-frame pose, so the tool tip lands at the same physical location. Change the TCP offset (or swap grippers) and your saved points stay valid.
|
|
581
646
|
|
|
582
647
|
```python
|
|
583
648
|
robot.save_point("here")
|
|
@@ -12,6 +12,7 @@ from __future__ import annotations
|
|
|
12
12
|
import argparse
|
|
13
13
|
import sys
|
|
14
14
|
|
|
15
|
+
from urkit.cli.init import init_command
|
|
15
16
|
from urkit.cli.teach import teach_command
|
|
16
17
|
from urkit.cli.points import points_command
|
|
17
18
|
|
|
@@ -25,6 +26,24 @@ def main() -> None:
|
|
|
25
26
|
|
|
26
27
|
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
27
28
|
|
|
29
|
+
# init subcommand
|
|
30
|
+
init_parser = subparsers.add_parser(
|
|
31
|
+
"init",
|
|
32
|
+
help="Scaffold a default config.yaml",
|
|
33
|
+
)
|
|
34
|
+
init_parser.add_argument(
|
|
35
|
+
"--output",
|
|
36
|
+
type=str,
|
|
37
|
+
default="config.yaml",
|
|
38
|
+
help="Output path (default: config.yaml in CWD)",
|
|
39
|
+
)
|
|
40
|
+
init_parser.add_argument(
|
|
41
|
+
"--force",
|
|
42
|
+
action="store_true",
|
|
43
|
+
default=False,
|
|
44
|
+
help="Overwrite existing file",
|
|
45
|
+
)
|
|
46
|
+
|
|
28
47
|
# teach subcommand
|
|
29
48
|
teach_parser = subparsers.add_parser(
|
|
30
49
|
"teach",
|
|
@@ -113,7 +132,9 @@ def main() -> None:
|
|
|
113
132
|
parser.print_help()
|
|
114
133
|
sys.exit(1)
|
|
115
134
|
|
|
116
|
-
if args.command == "
|
|
135
|
+
if args.command == "init":
|
|
136
|
+
init_command(args)
|
|
137
|
+
elif args.command == "teach":
|
|
117
138
|
teach_command(args)
|
|
118
139
|
elif args.command == "points":
|
|
119
140
|
points_command(args)
|
|
@@ -144,10 +144,17 @@ class ConnectionMonitor:
|
|
|
144
144
|
logger.info("Connection monitor loop exited")
|
|
145
145
|
|
|
146
146
|
def _trigger(self, reason: str) -> None:
|
|
147
|
-
"""Record fault reason and send SIGALRM to interrupt blocking calls.
|
|
147
|
+
"""Record fault reason and send SIGALRM to interrupt blocking calls.
|
|
148
|
+
|
|
149
|
+
On Windows there is no self-signal: the fault_detected flag is
|
|
150
|
+
the only interruption path, and callers poll it between and
|
|
151
|
+
after RTDE calls.
|
|
152
|
+
"""
|
|
148
153
|
self._reason = reason
|
|
149
154
|
self._fault_event.set()
|
|
150
155
|
logger.info("Fault detected: %s", reason)
|
|
156
|
+
if not hasattr(signal, "SIGALRM"):
|
|
157
|
+
return
|
|
151
158
|
try:
|
|
152
159
|
os.kill(os.getpid(), signal.SIGALRM)
|
|
153
160
|
except OSError as e:
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""urkit init — scaffold a default config.yaml file.
|
|
2
|
+
|
|
3
|
+
Generates a commented config file with all available options so the
|
|
4
|
+
user can edit it for their setup.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
urkit init # writes config.yaml in CWD
|
|
8
|
+
urkit init --output station_a.yaml # custom path
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
_DEFAULT_TEMPLATE = """\
|
|
17
|
+
# urkit config — edit and save as config.yaml
|
|
18
|
+
# Generated by: urkit init
|
|
19
|
+
#
|
|
20
|
+
# Load it:
|
|
21
|
+
# Python: robot = URRobot.from_config("config.yaml")
|
|
22
|
+
# CLI: urkit teach (auto-loads config.yaml)
|
|
23
|
+
# CLI: urkit teach --config station_a.yaml
|
|
24
|
+
|
|
25
|
+
# Robot IP address (required)
|
|
26
|
+
robot_ip: 192.168.1.50
|
|
27
|
+
|
|
28
|
+
# Path to SQLite points database (optional, created if missing)
|
|
29
|
+
points_path: points.db
|
|
30
|
+
|
|
31
|
+
# Gripper preset (optional)
|
|
32
|
+
# Built-in: hand-e, 2f-85, 2f-140
|
|
33
|
+
# Digital I/O: digital
|
|
34
|
+
# Omit or set to none for no gripper
|
|
35
|
+
gripper: none
|
|
36
|
+
|
|
37
|
+
# Gripper overrides (optional)
|
|
38
|
+
gripper_config:
|
|
39
|
+
# Physical properties (override preset defaults)
|
|
40
|
+
# mass: 1.0 # kg
|
|
41
|
+
# center_of_gravity: [0.0, 0.0, 0.0] # [x, y, z] in meters
|
|
42
|
+
# tcp_offset: [0.0, 0.0, 0.175, 0.0, 0.0, 0.0] # [x, y, z, rx, ry, rz]
|
|
43
|
+
|
|
44
|
+
# Robotiq-specific (hand-e, 2f-85, 2f-140)
|
|
45
|
+
# force: 100 # grip force 0-100
|
|
46
|
+
# speed: 100 # movement speed 0-100
|
|
47
|
+
# max_mm: 85 # max finger travel (e.g. 85 for 2F-85)
|
|
48
|
+
|
|
49
|
+
# Digital gripper
|
|
50
|
+
# pin: 0 # digital output pin
|
|
51
|
+
# close_on_high: true # true=HIGH closes, false=LOW closes
|
|
52
|
+
|
|
53
|
+
# IK reference posture (optional)
|
|
54
|
+
# Set to a saved point name to prevent elbow/wrist flipping between moves
|
|
55
|
+
# ik_reference: home
|
|
56
|
+
|
|
57
|
+
# Default motion parameters (optional)
|
|
58
|
+
# default_vel: 0.5 # m/s
|
|
59
|
+
# default_acc: 0.3 # m/s²
|
|
60
|
+
|
|
61
|
+
# Expert mode (optional) — disable safety speed clamping in CLI
|
|
62
|
+
# expert_mode: false
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def init_command(args: object) -> None:
|
|
67
|
+
"""Execute the init command to scaffold a default config file.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
args: Parsed arguments with 'output' and 'force' attributes.
|
|
71
|
+
"""
|
|
72
|
+
output = Path(getattr(args, "output", "config.yaml") or "config.yaml")
|
|
73
|
+
force = getattr(args, "force", False)
|
|
74
|
+
|
|
75
|
+
if output.exists() and not force:
|
|
76
|
+
print(f"Error: {output} already exists.")
|
|
77
|
+
print(" Remove it first or use --force to overwrite.")
|
|
78
|
+
sys.exit(1)
|
|
79
|
+
|
|
80
|
+
output.write_text(_DEFAULT_TEMPLATE)
|
|
81
|
+
print(f"Created {output}")
|
|
82
|
+
print("Edit it with your robot IP and gripper settings, then run:")
|
|
83
|
+
print(" urkit teach")
|
|
@@ -9,9 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import difflib
|
|
11
11
|
import io
|
|
12
|
-
import select
|
|
13
12
|
import sys
|
|
14
|
-
import termios
|
|
15
13
|
import time
|
|
16
14
|
from pathlib import Path
|
|
17
15
|
|
|
@@ -20,6 +18,7 @@ from rich.table import Table
|
|
|
20
18
|
|
|
21
19
|
from urkit.config import _load_config as load_config
|
|
22
20
|
from urkit.cli.colors import blue, cyan, dim, yellow
|
|
21
|
+
from urkit.cli.terminal import RawTerminal, read_burst, wait_input, warn_if_windows
|
|
23
22
|
from urkit.points import Points
|
|
24
23
|
|
|
25
24
|
|
|
@@ -29,6 +28,7 @@ def points_command(args) -> None:
|
|
|
29
28
|
Args:
|
|
30
29
|
args: Parsed arguments from argparse (with points subcommand attributes).
|
|
31
30
|
"""
|
|
31
|
+
warn_if_windows()
|
|
32
32
|
_explore_points(args)
|
|
33
33
|
|
|
34
34
|
|
|
@@ -120,12 +120,8 @@ def _interactive_points_filter(points_db: Points, all_points: list[str], points_
|
|
|
120
120
|
refresh_error = None
|
|
121
121
|
|
|
122
122
|
# Set terminal to raw mode
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
new_settings[3] = new_settings[3] & ~(termios.ICANON | termios.ECHO)
|
|
126
|
-
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, new_settings)
|
|
127
|
-
|
|
128
|
-
fd = sys.stdin.fileno()
|
|
123
|
+
raw = RawTerminal()
|
|
124
|
+
raw.enable()
|
|
129
125
|
|
|
130
126
|
try:
|
|
131
127
|
while True:
|
|
@@ -238,44 +234,41 @@ def _interactive_points_filter(points_db: Points, all_points: list[str], points_
|
|
|
238
234
|
last_refresh = time.time()
|
|
239
235
|
|
|
240
236
|
# Check for input (non-blocking to allow periodic refresh)
|
|
241
|
-
|
|
242
|
-
if not rlist:
|
|
237
|
+
if not wait_input(0.1):
|
|
243
238
|
continue
|
|
244
239
|
|
|
245
|
-
# Read input
|
|
240
|
+
# Read the full input burst. On Unix an arrow key arrives as
|
|
241
|
+
# \x1b[A in the same burst; on Windows the terminal shim
|
|
242
|
+
# translates arrow keys to the same byte sequence.
|
|
246
243
|
try:
|
|
247
|
-
|
|
244
|
+
text = read_burst(0.0).decode("ascii", errors="replace")
|
|
248
245
|
except Exception:
|
|
249
246
|
break
|
|
247
|
+
if not text:
|
|
248
|
+
continue
|
|
250
249
|
|
|
251
|
-
if
|
|
252
|
-
#
|
|
253
|
-
|
|
254
|
-
if
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
elif ch3 == "B": # Down arrow - scroll down
|
|
264
|
-
filtered = [p for p in all_points_sorted if filter_str == "" or filter_str.lower() in p.lower()]
|
|
265
|
-
scroll = min(len(filtered) - 1, scroll + 1) if filtered else 0
|
|
266
|
-
needs_redraw = True
|
|
267
|
-
except Exception:
|
|
268
|
-
break
|
|
250
|
+
if text.startswith("\x1b"): # ESC or arrow key or function key
|
|
251
|
+
# A bare ESC may still be the first byte of a sequence
|
|
252
|
+
# that arrives late; wait a bit before treating it as quit.
|
|
253
|
+
if text == "\x1b":
|
|
254
|
+
text = text + read_burst(0.2).decode("ascii", errors="replace")
|
|
255
|
+
if text.endswith("[A"): # Up arrow - scroll up
|
|
256
|
+
scroll = max(0, scroll - 1)
|
|
257
|
+
needs_redraw = True
|
|
258
|
+
elif text.endswith("[B"): # Down arrow - scroll down
|
|
259
|
+
filtered = [p for p in all_points_sorted if filter_str == "" or filter_str.lower() in p.lower()]
|
|
260
|
+
scroll = min(len(filtered) - 1, scroll + 1) if filtered else 0
|
|
261
|
+
needs_redraw = True
|
|
269
262
|
else:
|
|
270
|
-
#
|
|
263
|
+
# Just ESC (or an unrecognized sequence) — quit
|
|
271
264
|
break
|
|
272
|
-
elif
|
|
265
|
+
elif text == "\x7f" or text == "\x08": # Backspace
|
|
273
266
|
if filter_str:
|
|
274
267
|
filter_str = filter_str[:-1]
|
|
275
268
|
scroll = 0
|
|
276
269
|
needs_redraw = True
|
|
277
|
-
elif
|
|
278
|
-
filter_str +=
|
|
270
|
+
elif text.isprintable():
|
|
271
|
+
filter_str += text
|
|
279
272
|
scroll = 0
|
|
280
273
|
needs_redraw = True
|
|
281
274
|
|
|
@@ -283,7 +276,7 @@ def _interactive_points_filter(points_db: Points, all_points: list[str], points_
|
|
|
283
276
|
# Ctrl+C — exit gracefully without traceback
|
|
284
277
|
pass
|
|
285
278
|
finally:
|
|
286
|
-
|
|
279
|
+
raw.disable()
|
|
287
280
|
sys.stdout.write("\n")
|
|
288
281
|
|
|
289
282
|
|