urkit 0.3.12__tar.gz → 0.3.14__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.3.12 → urkit-0.3.14}/PKG-INFO +10 -7
  2. {urkit-0.3.12 → urkit-0.3.14}/README.md +9 -6
  3. {urkit-0.3.12 → urkit-0.3.14}/pyproject.toml +1 -1
  4. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/__init__.py +1 -1
  5. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/motion.py +16 -3
  6. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/points.py +1 -1
  7. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/robot.py +47 -47
  8. {urkit-0.3.12 → urkit-0.3.14}/src/urkit.egg-info/PKG-INFO +10 -7
  9. {urkit-0.3.12 → urkit-0.3.14}/setup.cfg +0 -0
  10. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/__main__.py +0 -0
  11. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/cli/__init__.py +0 -0
  12. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/cli/colors.py +0 -0
  13. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/cli/connection_monitor.py +0 -0
  14. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/cli/points.py +0 -0
  15. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/cli/teach.py +0 -0
  16. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/config.py +0 -0
  17. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/connection.py +0 -0
  18. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/exceptions.py +0 -0
  19. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/geometry.py +0 -0
  20. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/gripper/__init__.py +0 -0
  21. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/gripper/base.py +0 -0
  22. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/gripper/digital.py +0 -0
  23. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/gripper/presets.py +0 -0
  24. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/gripper/robotiq.py +0 -0
  25. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/gripper/robotiq_preamble.py +0 -0
  26. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/io.py +0 -0
  27. {urkit-0.3.12 → urkit-0.3.14}/src/urkit/telemetry.py +0 -0
  28. {urkit-0.3.12 → urkit-0.3.14}/src/urkit.egg-info/SOURCES.txt +0 -0
  29. {urkit-0.3.12 → urkit-0.3.14}/src/urkit.egg-info/dependency_links.txt +0 -0
  30. {urkit-0.3.12 → urkit-0.3.14}/src/urkit.egg-info/entry_points.txt +0 -0
  31. {urkit-0.3.12 → urkit-0.3.14}/src/urkit.egg-info/requires.txt +0 -0
  32. {urkit-0.3.12 → urkit-0.3.14}/src/urkit.egg-info/top_level.txt +0 -0
  33. {urkit-0.3.12 → urkit-0.3.14}/tests/test_exceptions.py +0 -0
  34. {urkit-0.3.12 → urkit-0.3.14}/tests/test_geometry.py +0 -0
  35. {urkit-0.3.12 → urkit-0.3.14}/tests/test_gripper.py +0 -0
  36. {urkit-0.3.12 → urkit-0.3.14}/tests/test_gripper_factory.py +0 -0
  37. {urkit-0.3.12 → urkit-0.3.14}/tests/test_gripper_presets.py +0 -0
  38. {urkit-0.3.12 → urkit-0.3.14}/tests/test_points.py +0 -0
  39. {urkit-0.3.12 → urkit-0.3.14}/tests/test_robot_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: urkit
3
- Version: 0.3.12
3
+ Version: 0.3.14
4
4
  Summary: Universal Robots e-Series control toolkit built on ur_rtde
5
5
  Author: URKit Contributors
6
6
  License: MIT
@@ -447,24 +447,27 @@ robot.move_relative(delta_z=0.05, frame=MoveFrame.TOOL) # 5cm along tool Z
447
447
  robot.move_relative([0, 0.01, 0, 0, 0, 0])
448
448
  ```
449
449
 
450
- #### Sequences with Blending
450
+ #### Sequences
451
451
 
452
452
  ```python
453
+ # Chain multiple moves into one call
453
454
  robot.move_sequence(["a", "b", "c"])
454
- robot.move_sequence(["a", "b", "c"], blend_radius=0.02)
455
455
  ```
456
456
 
457
457
  #### Contact Detection
458
458
 
459
459
  ```python
460
- # Zeros FT sensor automatically, then moves until force exceeds threshold
461
- robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
460
+ # Move straight down until contact (zeros FT sensor automatically)
461
+ robot.move_until_contact(speed_z=-0.02)
462
462
 
463
463
  # Custom threshold (default: 5.0 N/Nm)
464
- robot.move_until_contact([0, 0, -0.02, 0, 0, 0], threshold=10.0)
464
+ robot.move_until_contact(speed_z=-0.02, threshold=10.0)
465
465
 
466
466
  # Skip zeroing if you need absolute force values
467
- robot.move_until_contact([0, 0, -0.02, 0, 0, 0], zero_first=False)
467
+ robot.move_until_contact(speed_z=-0.02, zero_first=False)
468
+
469
+ # Full speed vector
470
+ robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
468
471
 
469
472
  # Manual zero (e.g. before custom force-based logic)
470
473
  robot.zero_ft_sensor()
@@ -421,24 +421,27 @@ robot.move_relative(delta_z=0.05, frame=MoveFrame.TOOL) # 5cm along tool Z
421
421
  robot.move_relative([0, 0.01, 0, 0, 0, 0])
422
422
  ```
423
423
 
424
- #### Sequences with Blending
424
+ #### Sequences
425
425
 
426
426
  ```python
427
+ # Chain multiple moves into one call
427
428
  robot.move_sequence(["a", "b", "c"])
428
- robot.move_sequence(["a", "b", "c"], blend_radius=0.02)
429
429
  ```
430
430
 
431
431
  #### Contact Detection
432
432
 
433
433
  ```python
434
- # Zeros FT sensor automatically, then moves until force exceeds threshold
435
- robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
434
+ # Move straight down until contact (zeros FT sensor automatically)
435
+ robot.move_until_contact(speed_z=-0.02)
436
436
 
437
437
  # Custom threshold (default: 5.0 N/Nm)
438
- robot.move_until_contact([0, 0, -0.02, 0, 0, 0], threshold=10.0)
438
+ robot.move_until_contact(speed_z=-0.02, threshold=10.0)
439
439
 
440
440
  # Skip zeroing if you need absolute force values
441
- robot.move_until_contact([0, 0, -0.02, 0, 0, 0], zero_first=False)
441
+ robot.move_until_contact(speed_z=-0.02, zero_first=False)
442
+
443
+ # Full speed vector
444
+ robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
442
445
 
443
446
  # Manual zero (e.g. before custom force-based logic)
444
447
  robot.zero_ft_sensor()
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "urkit"
7
- version = "0.3.12"
7
+ version = "0.3.14"
8
8
  description = "Universal Robots e-Series control toolkit built on ur_rtde"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -24,7 +24,7 @@ Quick start::
24
24
 
25
25
  from __future__ import annotations
26
26
 
27
- __version__ = "0.3.12"
27
+ __version__ = "0.3.14"
28
28
 
29
29
  from urkit.config import load_config, resolve_config
30
30
  from urkit.exceptions import (
@@ -7,6 +7,7 @@ velocity and acceleration override.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ import io
10
11
  import logging
11
12
  import os
12
13
  import sys
@@ -511,14 +512,26 @@ class Motion:
511
512
  else:
512
513
  raise MotionError(f"Unknown freedrive mode: {mode}")
513
514
 
514
- feature = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # base coordinate frame
515
- success = self._rtde_c.freedriveMode(free_axes, feature)
515
+ center = list(self._rtde_r.getActualTCPPose())
516
+ # Capture stderr from ur_rtde C++ library to surface robot errors
517
+ stderr_buf = io.StringIO()
518
+ old_stderr = sys.stderr
519
+ sys.stderr = stderr_buf
520
+ try:
521
+ success = self._rtde_c.freedriveMode(free_axes, center)
522
+ finally:
523
+ sys.stderr = old_stderr
524
+ stderr_text = stderr_buf.getvalue().strip()
525
+
516
526
  if not success:
527
+ detail = stderr_text if stderr_text else "no detail from robot"
517
528
  raise MotionError(
518
- f"freedriveMode returned false (mode={mode.name})"
529
+ f"freedriveMode returned false (mode={mode.name}): {detail}"
519
530
  )
520
531
  self._freedrive_active = True
521
532
  logger.info("Freedrive mode enabled (%s)", mode.name)
533
+ except MotionError:
534
+ raise
522
535
  except Exception as e:
523
536
  raise MotionError(
524
537
  f"Failed to enable freedrive mode: {e}"
@@ -119,7 +119,7 @@ class Points:
119
119
  else:
120
120
  self._path = Path(path).resolve()
121
121
  self._path.parent.mkdir(parents=True, exist_ok=True)
122
- self._conn = sqlite3.connect(str(self._path))
122
+ self._conn = sqlite3.connect(str(self._path), check_same_thread=False)
123
123
  _init_db(self._conn)
124
124
 
125
125
  def _close(self) -> None:
@@ -16,6 +16,7 @@ if TYPE_CHECKING:
16
16
  from rtde.control_interface import RTDEControlInterface
17
17
  from rtde.receive_interface import RTDEReceiveInterface
18
18
 
19
+ from urkit.config import resolve_config
19
20
  from urkit.connection import (
20
21
  _check_remote_mode,
21
22
  _connect_dashboard,
@@ -25,7 +26,13 @@ from urkit.connection import (
25
26
  _try_recover_safety,
26
27
  _validate_connection,
27
28
  )
28
- from urkit.exceptions import GripperError, MotionError, PointError, URKitConnectionError as ConnectionError
29
+ from urkit.exceptions import (
30
+ GripperError,
31
+ MotionError,
32
+ PointError,
33
+ RtdeRegisterConflictError,
34
+ URKitConnectionError as ConnectionError,
35
+ )
29
36
  from urkit.geometry import MoveFrame, transform_pose_delta
30
37
  from urkit.gripper.base import Gripper
31
38
  from urkit.gripper.presets import DigitalGripperConfig, GripperPreset, PRESETS
@@ -189,8 +196,6 @@ class URRobot:
189
196
 
190
197
  # Connect RTDE — retry, the Secondary Interface may need time to
191
198
  # release registers after program stop or boot.
192
- from urkit.exceptions import RtdeRegisterConflictError
193
-
194
199
  rtde_attempts = 2
195
200
  for attempt in range(1, rtde_attempts + 1):
196
201
  try:
@@ -371,15 +376,12 @@ class URRobot:
371
376
  speed: 80
372
377
  default_vel: 0.5
373
378
  default_acc: 0.3
374
- rtde_frequency: 500
375
379
 
376
380
  Example:
377
381
  >>> robot = URRobot.from_config("config.yaml")
378
382
  >>> robot = URRobot.from_config("config.yaml", ip="10.0.0.50")
379
383
  >>> robot = URRobot.from_config({"robot_ip": "192.168.1.50", "points_path": "points.db", "gripper": "2f-85"})
380
384
  """
381
- from urkit.config import resolve_config
382
-
383
385
  if isinstance(config, str):
384
386
  resolved = resolve_config(config)
385
387
  if resolved is None:
@@ -433,7 +435,9 @@ class URRobot:
433
435
  nested_cfg = cfg.get("gripper_config") or {}
434
436
  for key in gripper_overrides:
435
437
  if key not in gripper_kwargs:
436
- gripper_kwargs[key] = nested_cfg.get(key, cfg.get(key)) # type: ignore
438
+ value = nested_cfg.get(key, cfg.get(key)) # type: ignore
439
+ if value is not None:
440
+ gripper_kwargs[key] = value
437
441
 
438
442
  return cls(
439
443
  ip=resolved_ip,
@@ -1197,28 +1201,20 @@ class URRobot:
1197
1201
  acc: float | None = None,
1198
1202
  asynchronous: bool = False,
1199
1203
  ) -> None:
1200
- """Move through a sequence of points with optional blending.
1204
+ """Move through a sequence of points.
1201
1205
 
1202
- Executes the full path as a single RTDE command using ur_rtde's
1203
- Path API. When *blend_radius* is set (in meters), the robot
1204
- rounds corners instead of stopping at each intermediate waypoint —
1205
- the same blending you set on the UR teach pendant.
1206
-
1207
- The first and last points always use a blend radius of 0 so the
1208
- robot stops cleanly at the start and end of the sequence.
1206
+ Executes each target in order using individual moveL/moveJ calls.
1207
+ Convenience method to condense multiple moves into one call.
1209
1208
 
1210
1209
  Args:
1211
1210
  targets: List of saved point names or raw poses
1212
1211
  [x, y, z, rx, ry, rz].
1213
1212
  linear: If True (default), use Cartesian linear moves (moveL).
1214
1213
  If False, use joint-space moves (moveJ).
1215
- blend_radius: Blending radius in meters (default 0.0 = stop
1216
- at each point). Typical values: 0.001-0.1 (1mm-100mm).
1217
- Applied to intermediate points only.
1214
+ blend_radius: Currently ignored. Kept for API compatibility.
1218
1215
  vel: Velocity override. Falls back to default_vel.
1219
1216
  acc: Acceleration override. Falls back to default_acc.
1220
- asynchronous: If True, sequence runs in background and method
1221
- returns immediately (default False).
1217
+ asynchronous: Currently ignored.
1222
1218
 
1223
1219
  Raises:
1224
1220
  MotionError: If the sequence fails or fewer than 2 targets.
@@ -1227,10 +1223,6 @@ class URRobot:
1227
1223
  Example:
1228
1224
  >>> # Move through waypoints, stop at each
1229
1225
  >>> robot.move_sequence(["a", "b", "c"])
1230
- >>> # Smooth path with 2cm corner blending
1231
- >>> robot.move_sequence(["a", "b", "c"], blend_radius=0.02)
1232
- >>> # Joint-space sequence with blending
1233
- >>> robot.move_sequence(["a", "b", "c"], linear=False, blend_radius=0.05)
1234
1226
  """
1235
1227
  self._check_connection()
1236
1228
  self._disable_freedrive_guard()
@@ -1243,32 +1235,23 @@ class URRobot:
1243
1235
  v = vel if vel is not None else self._default_vel
1244
1236
  a = acc if acc is not None else self._default_acc
1245
1237
 
1246
- # Import here to avoid hard dependency at module level.
1247
- from rtde_control import Path, PathEntry # noqa: N812
1248
-
1249
- path = Path()
1250
- move_type = PathEntry.MoveL if linear else PathEntry.MoveJ
1251
-
1252
1238
  for i, target in enumerate(targets):
1253
1239
  point = self._lookup_point(target)
1254
- # First and last points: no blending (stop cleanly).
1255
- # Intermediate points: use the configured blend radius.
1256
- r = blend_radius if (0 < i < len(targets) - 1) else 0.0
1257
- entry_data = list(point.pose) + [v, a, r]
1258
- path.add_entry(
1259
- PathEntry(move_type, PathEntry.PositionTcpPose, entry_data)
1260
- )
1261
1240
  label = (
1262
1241
  f"'{target}'" if isinstance(target, str) else str(target[:3])
1263
1242
  )
1264
1243
  logger.info(
1265
- "move_sequence: %s (r=%.3f) (%d/%d)", label, r, i + 1, len(targets)
1244
+ "move_sequence: %s (%d/%d)", label, i + 1, len(targets)
1266
1245
  )
1267
-
1268
- try:
1269
- self._rtde_c.movePath(path, not asynchronous)
1270
- except Exception as e:
1271
- raise MotionError(f"move_sequence failed: {e}")
1246
+ try:
1247
+ if linear:
1248
+ self._rtde_c.moveL(list(point.pose), v, a)
1249
+ else:
1250
+ self._rtde_c.moveJ_IK(
1251
+ list(point.pose), self._rtde_r.getActualQ(), v, a
1252
+ )
1253
+ except Exception as e:
1254
+ raise MotionError(f"move_sequence failed at target {i}: {e}")
1272
1255
 
1273
1256
  def zero_ft_sensor(self) -> None:
1274
1257
  """Zero the robot's force/torque sensor.
@@ -1285,11 +1268,17 @@ class URRobot:
1285
1268
 
1286
1269
  def move_until_contact(
1287
1270
  self,
1288
- speed_vector: list[float],
1271
+ speed_vector: list[float] | None = None,
1289
1272
  *,
1290
1273
  threshold: float = 5.0,
1291
1274
  acceleration: float = 0.1,
1292
1275
  zero_first: bool = True,
1276
+ speed_x: float = 0.0,
1277
+ speed_y: float = 0.0,
1278
+ speed_z: float = 0.0,
1279
+ speed_rx: float = 0.0,
1280
+ speed_ry: float = 0.0,
1281
+ speed_rz: float = 0.0,
1293
1282
  ) -> None:
1294
1283
  """Move until contact is detected via TCP force sensing.
1295
1284
 
@@ -1299,23 +1288,34 @@ class URRobot:
1299
1288
  Args:
1300
1289
  speed_vector: 6-element speed vector
1301
1290
  ``[vx, vy, vz, vRoll, vPitch, dYaw]`` in m/s and rad/s.
1291
+ Mutually exclusive with individual speed_* parameters.
1302
1292
  threshold: Force/torque delta (N or Nm) that triggers contact.
1303
1293
  Contact fires when any wrench component changes by more
1304
1294
  than this value from the baseline reading.
1305
1295
  acceleration: Acceleration limit passed to ``speedL()`` in m/s².
1306
1296
  zero_first: If True (default), zero the FT sensor before reading
1307
1297
  the baseline. Set to False if you need absolute force values.
1298
+ speed_x, speed_y, speed_z: Linear speed components in m/s.
1299
+ speed_rx, speed_ry, speed_rz: Angular speed components in rad/s.
1308
1300
 
1309
1301
  Example:
1310
- >>> # Move straight down until contact (zeros FT sensor first)
1302
+ >>> # Move straight down until contact
1303
+ >>> robot.move_until_contact(speed_z=-0.02)
1304
+ >>> # Using full speed vector
1311
1305
  >>> robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
1312
1306
  >>> # Higher threshold for heavier contact
1313
- >>> robot.move_until_contact([0, 0, -0.02, 0, 0, 0], threshold=10.0)
1307
+ >>> robot.move_until_contact(speed_z=-0.02, threshold=10.0)
1314
1308
  """
1315
1309
  self._check_connection()
1316
1310
  self._disable_freedrive_guard()
1311
+
1312
+ if speed_vector is not None:
1313
+ final_vector = speed_vector
1314
+ else:
1315
+ final_vector = [speed_x, speed_y, speed_z, speed_rx, speed_ry, speed_rz]
1316
+
1317
1317
  self._motion.move_until_contact(
1318
- speed_vector,
1318
+ final_vector,
1319
1319
  threshold=threshold,
1320
1320
  acceleration=acceleration,
1321
1321
  zero_first=zero_first,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: urkit
3
- Version: 0.3.12
3
+ Version: 0.3.14
4
4
  Summary: Universal Robots e-Series control toolkit built on ur_rtde
5
5
  Author: URKit Contributors
6
6
  License: MIT
@@ -447,24 +447,27 @@ robot.move_relative(delta_z=0.05, frame=MoveFrame.TOOL) # 5cm along tool Z
447
447
  robot.move_relative([0, 0.01, 0, 0, 0, 0])
448
448
  ```
449
449
 
450
- #### Sequences with Blending
450
+ #### Sequences
451
451
 
452
452
  ```python
453
+ # Chain multiple moves into one call
453
454
  robot.move_sequence(["a", "b", "c"])
454
- robot.move_sequence(["a", "b", "c"], blend_radius=0.02)
455
455
  ```
456
456
 
457
457
  #### Contact Detection
458
458
 
459
459
  ```python
460
- # Zeros FT sensor automatically, then moves until force exceeds threshold
461
- robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
460
+ # Move straight down until contact (zeros FT sensor automatically)
461
+ robot.move_until_contact(speed_z=-0.02)
462
462
 
463
463
  # Custom threshold (default: 5.0 N/Nm)
464
- robot.move_until_contact([0, 0, -0.02, 0, 0, 0], threshold=10.0)
464
+ robot.move_until_contact(speed_z=-0.02, threshold=10.0)
465
465
 
466
466
  # Skip zeroing if you need absolute force values
467
- robot.move_until_contact([0, 0, -0.02, 0, 0, 0], zero_first=False)
467
+ robot.move_until_contact(speed_z=-0.02, zero_first=False)
468
+
469
+ # Full speed vector
470
+ robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
468
471
 
469
472
  # Manual zero (e.g. before custom force-based logic)
470
473
  robot.zero_ft_sensor()
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