urkit 0.2.1__tar.gz → 3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. {urkit-0.2.1 → urkit-3.1}/PKG-INFO +165 -54
  2. {urkit-0.2.1 → urkit-3.1}/README.md +164 -53
  3. {urkit-0.2.1 → urkit-3.1}/pyproject.toml +5 -1
  4. {urkit-0.2.1 → urkit-3.1}/src/urkit/__init__.py +2 -4
  5. {urkit-0.2.1 → urkit-3.1}/src/urkit/__main__.py +8 -2
  6. {urkit-0.2.1 → urkit-3.1}/src/urkit/cli/points.py +51 -55
  7. {urkit-0.2.1 → urkit-3.1}/src/urkit/cli/teach.py +299 -56
  8. {urkit-0.2.1 → urkit-3.1}/src/urkit/config.py +1 -1
  9. {urkit-0.2.1 → urkit-3.1}/src/urkit/geometry.py +6 -6
  10. {urkit-0.2.1 → urkit-3.1}/src/urkit/gripper/__init__.py +2 -2
  11. {urkit-0.2.1 → urkit-3.1}/src/urkit/gripper/base.py +12 -23
  12. {urkit-0.2.1 → urkit-3.1}/src/urkit/gripper/digital.py +1 -1
  13. {urkit-0.2.1 → urkit-3.1}/src/urkit/gripper/presets.py +2 -2
  14. {urkit-0.2.1 → urkit-3.1}/src/urkit/gripper/robotiq.py +6 -3
  15. {urkit-0.2.1 → urkit-3.1}/src/urkit/motion.py +15 -9
  16. {urkit-0.2.1 → urkit-3.1}/src/urkit/points.py +4 -3
  17. {urkit-0.2.1 → urkit-3.1}/src/urkit/robot.py +98 -44
  18. {urkit-0.2.1 → urkit-3.1}/src/urkit/telemetry.py +16 -17
  19. {urkit-0.2.1 → urkit-3.1}/src/urkit.egg-info/PKG-INFO +165 -54
  20. {urkit-0.2.1 → urkit-3.1}/tests/test_robot_integration.py +1 -5
  21. {urkit-0.2.1 → urkit-3.1}/setup.cfg +0 -0
  22. {urkit-0.2.1 → urkit-3.1}/src/urkit/cli/__init__.py +0 -0
  23. {urkit-0.2.1 → urkit-3.1}/src/urkit/cli/colors.py +0 -0
  24. {urkit-0.2.1 → urkit-3.1}/src/urkit/cli/connection_monitor.py +0 -0
  25. {urkit-0.2.1 → urkit-3.1}/src/urkit/connection.py +0 -0
  26. {urkit-0.2.1 → urkit-3.1}/src/urkit/exceptions.py +0 -0
  27. {urkit-0.2.1 → urkit-3.1}/src/urkit/gripper/robotiq_preamble.py +0 -0
  28. {urkit-0.2.1 → urkit-3.1}/src/urkit/io.py +0 -0
  29. {urkit-0.2.1 → urkit-3.1}/src/urkit.egg-info/SOURCES.txt +0 -0
  30. {urkit-0.2.1 → urkit-3.1}/src/urkit.egg-info/dependency_links.txt +0 -0
  31. {urkit-0.2.1 → urkit-3.1}/src/urkit.egg-info/entry_points.txt +0 -0
  32. {urkit-0.2.1 → urkit-3.1}/src/urkit.egg-info/requires.txt +0 -0
  33. {urkit-0.2.1 → urkit-3.1}/src/urkit.egg-info/top_level.txt +0 -0
  34. {urkit-0.2.1 → urkit-3.1}/tests/test_exceptions.py +0 -0
  35. {urkit-0.2.1 → urkit-3.1}/tests/test_geometry.py +0 -0
  36. {urkit-0.2.1 → urkit-3.1}/tests/test_gripper.py +0 -0
  37. {urkit-0.2.1 → urkit-3.1}/tests/test_gripper_factory.py +0 -0
  38. {urkit-0.2.1 → urkit-3.1}/tests/test_gripper_presets.py +0 -0
  39. {urkit-0.2.1 → urkit-3.1}/tests/test_points.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: urkit
3
- Version: 0.2.1
3
+ Version: 3.1
4
4
  Summary: Universal Robots e-Series control toolkit built on ur_rtde
5
5
  Author: URKit Contributors
6
6
  License: MIT
@@ -58,14 +58,19 @@ Built on [`ur_rtde`](https://sdurobotics.gitlab.io/ur_rtde/), it packages the op
58
58
  ## Quick Start
59
59
 
60
60
  ```bash
61
- pip install urkit
61
+ pip install -U urkit
62
62
  ```
63
63
 
64
+ The `-U` (upgrade) flag ensures you always get the latest version — this project is in early development and changes frequently.
65
+
64
66
  Requires Python 3.8+ and a Universal Robots e-Series (UR3e to UR30).
65
67
 
66
68
  ### Robot Setup (one-time)
67
69
 
68
- 1. **Network**: `☰` → `System` → `Network`: set the robot's IP and subnet.
70
+ 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:
71
+ - **Robot**: `172.31.1.42` / Subnet `255.255.255.0`
72
+ - **PC**: `172.31.1.1` / Netmask `255.255.255.0`
73
+ - Verify with `ping 172.31.1.42`. Connect via direct Ethernet cable or a switch.
69
74
  2. **Remote Control**: `☰` → `System` → `Remote Control`: Enable. Press the remote/local button on the pendant.
70
75
  3. **Security**: `☰` → `Security` → `Services`: enable RTDE and disable EtherNet/IP, PROFINET, or MODBUS if they're claiming RTDE registers. Save and restart.
71
76
 
@@ -76,9 +81,9 @@ That's it. No `.urp` files to run, no extra programs needed.
76
81
  If you have a Robotiq gripper, install the **Robotiq Gripper Control** URCap first: download from [robotiq.com/support](https://robotiq.com/support), copy the `.urcap` to a USB drive, and install via `☰` → `Settings` → `System` → `URCaps`.
77
82
 
78
83
  ```python
79
- from urkit import URRobot, ROBOTIQ_2F_85 # or ROBOTIQ_2F_140, ROBOTIQ_HAND_E, or gripper=none
84
+ from urkit import URRobot, ROBOTIQ_HAND_E # or ROBOTIQ_2F_85, ROBOTIQ_2F_140, or gripper=None
80
85
 
81
- robot = URRobot(ip="172.31.1.200", points="points.db", gripper=ROBOTIQ_2F_85)
86
+ robot = URRobot(ip="172.31.1.42", points="points.db", gripper=ROBOTIQ_HAND_E)
82
87
  robot.gripper.activate()
83
88
 
84
89
  robot.move_to("home")
@@ -90,9 +95,9 @@ robot.gripper.open()
90
95
 
91
96
  The typical workflow:
92
97
 
93
- 1. **Teach points** use the CLI to position the robot and save named waypoints.
94
- 2. **Write code** create a robot, move to points by name, apply offsets, run sequences.
95
- 3. **Iterate** add more points, tweak your code, repeat.
98
+ 1. **Teach points.** Use the CLI to position the robot and save named waypoints.
99
+ 2. **Write code.** Create a robot, move to points by name, apply offsets, run sequences.
100
+ 3. **Iterate.** Add more points, tweak your code, repeat.
96
101
 
97
102
  ---
98
103
 
@@ -105,8 +110,8 @@ URKit provides two CLI tools: **teach** for interactive robot control, and **poi
105
110
  The interactive teach pendant for moving the robot, saving points, and checking telemetry:
106
111
 
107
112
  ```bash
108
- urkit teach 172.31.1.200 # with robot IP
109
- urkit teach # reads IP from config.yaml
113
+ urkit teach 172.31.1.42 # with robot IP
114
+ urkit teach # reads IP from config.yaml
110
115
  ```
111
116
 
112
117
  **Flags:**
@@ -121,25 +126,18 @@ urkit teach # reads IP from config.yaml
121
126
  | `--gripper-close-on-high` | Digital polarity: `true` or `false` |
122
127
  | `--points` | Path to `points.db` file (overrides config) |
123
128
  | `--config` | Path to config file (default: `config.yaml` in project root or CWD) |
129
+ | `-e`, `--expert` | Disable safety speed clamping (full speed for goto/tcp-down) |
124
130
  | `-v`, `--verbose` | Show verbose output (debug connection issues) |
125
131
 
126
132
  ### Points Explorer
127
133
 
128
- Browse saved waypoints with real-time search filtering no robot connection needed:
134
+ Browse saved waypoints with real-time search filtering. No robot connection needed:
129
135
 
130
136
  ```bash
131
137
  urkit points # uses default points.db
132
138
  urkit points test_points.db # use specific database
133
139
  ```
134
140
 
135
- **Features:**
136
- - **Type to search** — real-time substring filtering
137
- - **Fuzzy matching** — type `pk` to find `pick_1` (>60% match)
138
- - **Smart sorting** — exact prefix matches first, then substring, then fuzzy
139
- - **Spatial sorting** — points ordered by proximity to "home" point (XYZ distance)
140
- - **Theme-aware** — automatically adapts colors for light/dark terminals
141
- - **Arrow keys** — scroll · **ESC** — quit
142
-
143
141
  ### Key Map
144
142
 
145
143
  All movement and orientation keys support **hold-to-repeat**.
@@ -197,7 +195,7 @@ All movement and orientation keys support **hold-to-repeat**.
197
195
  <table>
198
196
  <tr><th>Key</th><th>Action</th></tr>
199
197
  <tr><td><code>B</code></td><td><strong>Save</strong> current position</td></tr>
200
- <tr><td><code>G</code></td><td>Go to saved point</td></tr>
198
+ <tr><td><code>G</code></td><td>Go to saved point (<code>Space</code> to cancel mid-move, <code>ESC</code> to exit)</td></tr>
201
199
  <tr><td><code>H</code></td><td>Delete saved point</td></tr>
202
200
  <tr><td><code>P</code></td><td>Open points explorer</td></tr>
203
201
  <tr><td><code>R</code></td><td>Rename saved point</td></tr>
@@ -217,6 +215,52 @@ All movement and orientation keys support **hold-to-repeat**.
217
215
  </tr>
218
216
  </table>
219
217
 
218
+ ### Joint Display
219
+
220
+ The teach pendant shows live joint angles alongside TCP position and orientation:
221
+
222
+ ```
223
+ Position X=+0.432 Y=+0.111 Z=+0.227
224
+ Orientation R=+131.3 P=-121.0 Y= +8.0
225
+ Joints J1=+150.0 J2=+020.0 J3=+160.0
226
+ J4=+050.0 J5=-080.0 J6=+157.0
227
+ ```
228
+
229
+ Joint angles color-code proximity to mechanical limits:
230
+
231
+ - **Yellow**: within 10% of joint range (warning)
232
+ - **Red**: within 5% of joint range (danger)
233
+
234
+ UR e-Series joint limits:
235
+
236
+ | Joint | Range | Notes |
237
+ |-------|-------|-------|
238
+ | J1 (shoulder pan) | ±360° | Full rotation |
239
+ | J2 (shoulder lift) | ±360° | Full rotation |
240
+ | J3 (elbow) | ±180° | Physically restricted — shoulder lift gets in the way |
241
+ | J4 (wrist 1) | ±360° | Full rotation |
242
+ | J5 (wrist 2) | ±360° | Full rotation |
243
+ | J6 (wrist 3) | ±360° | Tool flange unlimited rotation |
244
+
245
+ Thresholds scale with each joint's range, so warning zones feel proportional across all joints.
246
+
247
+ ### Safety
248
+
249
+ By default, **Go To** and **TCP Down** movements use a slow velocity (0.125 m/s) so its safer for anyone standing near the robot. The user's speed slider still applies as a global multiplier on top of this.
250
+
251
+ Delta movements (W/S/A/D/Q/E) use step-size-based velocities that scale with the speed slider set by the user.
252
+
253
+ To disable the slow default and use full speed, pass `--expert` or set `expert_mode: true` in your config:
254
+
255
+ ```bash
256
+ urkit teach 172.31.1.42 --expert
257
+ ```
258
+
259
+ ```yaml
260
+ # config.yaml
261
+ expert_mode: true
262
+ ```
263
+
220
264
  ---
221
265
 
222
266
  ## API Reference
@@ -226,14 +270,14 @@ All movement and orientation keys support **hold-to-repeat**.
226
270
  ```python
227
271
  from urkit import URRobot, ROBOTIQ_HAND_E
228
272
 
229
- robot = URRobot(ip="172.31.1.200", points="points.db", gripper=ROBOTIQ_HAND_E)
273
+ robot = URRobot(ip="172.31.1.42", points="points.db", gripper=ROBOTIQ_HAND_E)
230
274
  ```
231
275
 
232
276
  With custom motion defaults:
233
277
 
234
278
  ```python
235
279
  robot = URRobot(
236
- ip="172.31.1.200",
280
+ ip="172.31.1.42",
237
281
  points="points.db",
238
282
  gripper=ROBOTIQ_HAND_E,
239
283
  default_vel=0.5, # m/s
@@ -266,39 +310,41 @@ robot.gripper.open() # fully open (blocking by default)
266
310
  robot.gripper.close() # fully closed, stops on contact
267
311
  robot.gripper.open(wait=False) # non-blocking return
268
312
  robot.gripper.set_position(20) # 20mm open (Robotiq only, 0 = closed)
269
- robot.gripper.set_force(50) # grip force: 0-100
270
- robot.gripper.set_speed(80) # movement speed: 0-100
313
+ robot.gripper.set_force(50) # grip force: 0-100 (Robotiq only)
314
+ robot.gripper.set_speed(80) # movement speed: 0-100 (Robotiq only)
271
315
  ```
272
316
 
273
317
  Override preset values for custom fingers:
274
318
 
275
319
  ```python
276
- robot = URRobot(ip="172.31.1.200", points="points.db", gripper=ROBOTIQ_HAND_E, max_mm=120)
320
+ robot = URRobot(ip="172.31.1.42", points="points.db", gripper=ROBOTIQ_HAND_E, max_mm=120)
277
321
  ```
278
322
 
279
323
  #### Digital I/O Grippers
280
324
 
281
- For suction cups, solenoids, or custom actuators:
325
+ Robotiq grippers use a serial protocol over the robot's RS485 port. If you have a suction cup, solenoid, or any actuator controlled by a single digital output pin, use `DigitalGripperConfig` instead. It just turns that pin on (close) and off (open).
282
326
 
283
327
  ```python
284
328
  from urkit import URRobot, DigitalGripperConfig
285
329
 
286
330
  robot = URRobot(
287
- ip="172.31.1.200",
331
+ ip="172.31.1.42",
288
332
  points="points.db",
289
- gripper=DigitalGripperConfig(pin=3),
333
+ gripper=DigitalGripperConfig(pin=3), # pin 3 goes high = close
290
334
  )
291
335
 
292
- robot.gripper.open() # turn pin off (release)
293
- robot.gripper.close() # turn pin on (grab)
336
+ robot.gripper.open() # turn pin 3 off
337
+ robot.gripper.close() # turn pin 3 on
294
338
  ```
295
339
 
340
+ `set_force()` and `set_speed()` are not available for digital grippers.
341
+
296
342
  ### Points & Motion
297
343
 
298
- The points database is optional create a robot without one and attach later:
344
+ The points database is optional. Create a robot without one and attach later:
299
345
 
300
346
  ```python
301
- robot = URRobot(ip="172.31.1.200")
347
+ robot = URRobot(ip="172.31.1.42")
302
348
  robot.points_db = "points.db"
303
349
  ```
304
350
 
@@ -308,23 +354,50 @@ robot.points_db = "points.db"
308
354
  robot.move_to("pick") # linear move (default)
309
355
  robot.move_to("pick", linear=False) # joint move
310
356
  robot.move_to("pick", vel=1.0, acc=0.5) # override speed
357
+ robot.move_to("pick", asynchronous=True) # non-blocking, returns immediately
311
358
  ```
312
359
 
313
- #### Raw Poses
360
+ - **Linear (moveL):** TCP moves in a straight line. Predictable path, slower near complex orientations.
361
+ - **Joint (moveJ):** Each joint moves simultaneously. Faster, but the TCP follows an arc.
362
+ - **Asynchronous:** When `asynchronous=True`, the move runs in the UR controller's background thread and the method returns immediately.
363
+
364
+ #### Non-Blocking Moves
365
+
366
+ Start a move and do other things while it runs:
314
367
 
315
368
  ```python
316
- robot.move_to([0.5, 0, 0.3, 0, 0, 0]) # [x, y, z, rx, ry, rz]
369
+ robot.move_to("pick", asynchronous=True)
370
+
371
+ # Poll until done
372
+ while robot.is_moving():
373
+ time.sleep(0.01)
374
+
375
+ # Or cancel mid-move
376
+ robot.move_to("pick", asynchronous=True)
377
+ while robot.is_moving():
378
+ if should_cancel:
379
+ robot.stop() # sends stopL + stopJ
380
+ break
381
+ time.sleep(0.01)
317
382
  ```
318
383
 
384
+ **Teach pendant Go To** uses this pattern internally — Space cancels the move and returns to the menu.
385
+
386
+ #### Pose Format
387
+
388
+ A pose is `[x, y, z, rx, ry, rz]`: position in meters and orientation as a **rotation vector** (axis-angle in radians). This is not RPY (roll/pitch/yaw). The teach pendant displays RPY in degrees, which is a different representation. Values you see on the pendant won't match `get_tcp_pose()` directly.
389
+
319
390
  #### Offsets
320
391
 
392
+ Offsets are 6-element lists `[dx, dy, dz, drx, dry, drz]`:
393
+
321
394
  ```python
322
395
  robot.move_to("pick", offset=[0, 0, 0.05, 0, 0, 0]) # 5cm above pick
323
396
  ```
324
397
 
325
398
  #### Resolve a Pose
326
399
 
327
- Get a pose without moving useful for logging, comparisons, or custom motion:
400
+ Get a pose without moving. Useful for logging, comparisons, or custom motion:
328
401
 
329
402
  ```python
330
403
  pose = robot.get_pose("pick")
@@ -338,11 +411,15 @@ robot.move_to(pose) # move to the resolved pose later
338
411
  from urkit import MoveFrame
339
412
 
340
413
  robot.move_frame = MoveFrame.TOOL # default is BASE
341
- robot.move_to("pick", offset=[0, 0, 0.05]) # 5cm along tool Z
414
+ robot.move_relative([0, 0, 0.05, 0, 0, 0]) # 5cm along tool Z
342
415
  ```
343
416
 
344
- - **BASE** (default): offset relative to robot base
345
- - **TOOL**: offset relative to TCP orientation
417
+ - **BASE** (default): delta relative to robot base
418
+ - **TOOL**: delta relative to TCP orientation
419
+
420
+ #### Points are tool-agnostic
421
+
422
+ Points are stored in the active TCP frame, so they work with any tool. If you swap grippers and set the correct TCP offset, your saved points remain valid.
346
423
 
347
424
  #### Point Management
348
425
 
@@ -359,7 +436,7 @@ robot.import_points("backup.json")
359
436
 
360
437
  ```python
361
438
  robot.move_relative([0, 0.01, 0, 0, 0, 0]) # 1cm along Y
362
- robot.move_relative([0, 0, 0.05], frame=MoveFrame.TOOL)
439
+ robot.move_relative([0, 0, 0.05, 0, 0, 0], frame=MoveFrame.TOOL) # 5cm along tool Z
363
440
  ```
364
441
 
365
442
  #### Sequences with Blending
@@ -372,7 +449,7 @@ robot.move_sequence(["a", "b", "c"], blend_radius=0.02)
372
449
  #### Contact Detection
373
450
 
374
451
  ```python
375
- robot.move_until_contact([0, 0, -0.02, 0, 0, 0]) # Ctrl+C to stop
452
+ robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
376
453
  ```
377
454
 
378
455
  #### Velocity Control
@@ -396,11 +473,13 @@ robot.is_freedrive_active # check state
396
473
  #### Speed Control
397
474
 
398
475
  ```python
399
- robot.speed_stop() # emergency stop
400
- robot.set_speed_slider(0.5) # 50% hardware velocity cap
476
+ robot.stop() # stop current move immediately (stopL + stopJ)
477
+ robot.speed_stop() # stop velocity-controlled motion (not E-stop)
478
+ robot.set_speed_slider(0.5) # 50% velocity cap
479
+ robot.get_speed_slider() # read current slider (0.0-1.0)
401
480
  ```
402
481
 
403
- The speed slider is a hardware-level multiplier — same as the physical slider on the pendant. It's global, persistent, and affects all motion commands.
482
+ The speed slider controls the pendant's speed multiplier. It's global, persistent, and affects all motion commands.
404
483
 
405
484
  #### Inverse Kinematics
406
485
 
@@ -415,11 +494,27 @@ pose = robot.get_tcp_pose() # [x, y, z, rx, ry, rz]
415
494
  joints = robot.get_joint_positions() # [j0..j5]
416
495
  force = robot.get_tcp_force() # [fx, fy, fz, mx, my, mz]
417
496
  mode = robot.get_robot_mode() # "REMOTE_CONTROL", "SERVOING", etc.
418
- scaling = robot.get_speed_scaling() # 0.0-1.0
419
497
  payload = robot.get_payload() # kg
498
+ robot.is_moving() # bool — any joint/TCP velocity non-zero
420
499
  robot.is_protective_stopped() # bool
421
500
  robot.is_emergency_stopped() # bool
422
501
  robot.current_point() # {"pose": [...], "joints": [...]}
502
+ robot.is_at_pose(target) # bool — TCP within 1mm / 0.5°
503
+ robot.is_at_joints(target) # bool — all joints within 0.001 rad
504
+ ```
505
+
506
+ `is_moving()` is the simplest way to poll — `is_at_pose()` / `is_at_joints()` for precise target checking:
507
+
508
+ ```python
509
+ robot.move_to("pick", asynchronous=True)
510
+
511
+ # Simple: wait until robot stops
512
+ while robot.is_moving():
513
+ time.sleep(0.01)
514
+
515
+ # Precise: check against specific target
516
+ while not robot.is_at_pose(target_pose):
517
+ time.sleep(0.01)
423
518
  ```
424
519
 
425
520
  ### Digital I/O
@@ -453,11 +548,12 @@ URKit searches for `config.yaml` in this order:
453
548
 
454
549
  | Key | Description | Example |
455
550
  |-----|-------------|---------|
456
- | `robot_ip` | Robot IP address | `192.168.1.100` |
551
+ | `robot_ip` | Robot IP address | `172.31.1.42` |
457
552
  | `points_path` | Path to SQLite points database | `points.db` |
458
553
  | `gripper` | Gripper preset name | `hand-e`, `2f-85`, `2f-140`, `digital` |
459
554
  | `default_vel` | Default linear velocity (m/s) | `0.5` |
460
555
  | `default_acc` | Default linear acceleration (m/s²) | `0.3` |
556
+ | `expert_mode` | Disable safety speed clamping | `false` |
461
557
 
462
558
  ### Gripper Config
463
559
 
@@ -477,17 +573,17 @@ gripper_config:
477
573
 
478
574
  ### CLI Override Precedence
479
575
 
480
- 1. **CLI flags** `urkit teach 172.31.1.200 --gripper none`
481
- 2. **Config file** values from `config.yaml`
482
- 3. **Built-in defaults** `points.db`, no gripper, 0.5 m/s velocity
576
+ 1. **CLI flags.** `urkit teach 172.31.1.42 --gripper none`
577
+ 2. **Config file.** Values from `config.yaml`
578
+ 3. **Built-in defaults.** `points.db`, no gripper, 0.5 m/s velocity
483
579
 
484
580
  ### Saving Config
485
581
 
486
582
  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.
487
583
 
488
584
  ```bash
489
- urkit teach 172.31.1.200 --gripper hand-e # test, then press Y
490
- urkit teach # next time: reads from config
585
+ urkit teach 172.31.1.42 --gripper hand-e # test, then press Y
586
+ urkit teach # next time: reads from config
491
587
  ```
492
588
 
493
589
  Multiple workcells:
@@ -505,7 +601,7 @@ from urkit import load_config, resolve_config
505
601
  config = load_config() # auto-resolve
506
602
  config = load_config("/path/to/my.yaml") # explicit path
507
603
  path = resolve_config() # returns Path or None
508
- robot = URRobot.from_config({"robot_ip": "172.31.1.200", "gripper": "2f-85"})
604
+ robot = URRobot.from_config({"robot_ip": "172.31.1.42", "gripper": "2f-85"})
509
605
  ```
510
606
 
511
607
  ---
@@ -517,7 +613,6 @@ robot = URRobot.from_config({"robot_ip": "172.31.1.200", "gripper": "2f-85"})
517
613
  URKit doesn't try to wrap everything. Access the raw `ur_rtde` interfaces for advanced features:
518
614
 
519
615
  ```python
520
- robot.rtde_control.moveUntilContact([0, 0, -0.02, 0, 0, 0])
521
616
  robot.rtde_control.forceMode(...)
522
617
  robot.rtde_control.servoJ(...)
523
618
  robot.rtde_receive.getActualCurrent()
@@ -537,10 +632,10 @@ robot.reconnect_rtde() # reconnect after a drop
537
632
  ### Error Handling
538
633
 
539
634
  ```python
540
- from urkit import URKitError, RobotNotInRemoteModeError, RtdeRegisterConflictError
635
+ from urkit import URKitError, MotionError, PointError
541
636
 
542
637
  try:
543
- robot = URRobot(ip="172.31.1.200", points="points.db")
638
+ robot = URRobot(ip="172.31.1.42", points="points.db")
544
639
  except RobotNotInRemoteModeError:
545
640
  print("Enable remote control on the teach pendant!")
546
641
  except RtdeRegisterConflictError:
@@ -548,3 +643,19 @@ except RtdeRegisterConflictError:
548
643
  except URKitError as e:
549
644
  print(f"Error: {e}")
550
645
  ```
646
+
647
+ Common runtime errors:
648
+
649
+ | Exception | When |
650
+ |-----------|------|
651
+ | `MotionError` | Unreachable pose, bad TCP offset, freedrive failure |
652
+ | `PointError` | Point not found, no points database configured |
653
+ | `GripperError` | Gripper activation or communication failure |
654
+ | `URKitIOError` | Invalid pin number, I/O read/write failure |
655
+ | `TelemetryError` | Cannot read pose, joints, force, etc. |
656
+
657
+ When the robot enters protective stop or the RTDE connection drops, motion commands raise `URKitConnectionError` and the program should exit. The CLI handles this automatically.
658
+
659
+ ### Connection Notes
660
+
661
+ The `URRobot` constructor takes a few seconds on first call: it validates the connection, checks remote mode, powers on the robot, releases brakes, and connects RTDE. Subsequent calls are faster if the robot is already running.