urkit 0.3.22__tar.gz → 0.3.23__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 (40) hide show
  1. {urkit-0.3.22 → urkit-0.3.23}/PKG-INFO +11 -1
  2. {urkit-0.3.22 → urkit-0.3.23}/README.md +10 -0
  3. {urkit-0.3.22 → urkit-0.3.23}/pyproject.toml +1 -1
  4. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/__init__.py +1 -1
  5. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/motion.py +57 -4
  6. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/robot.py +16 -2
  7. {urkit-0.3.22 → urkit-0.3.23}/src/urkit.egg-info/PKG-INFO +11 -1
  8. {urkit-0.3.22 → urkit-0.3.23}/setup.cfg +0 -0
  9. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/__main__.py +0 -0
  10. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/cli/__init__.py +0 -0
  11. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/cli/colors.py +0 -0
  12. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/cli/connection_monitor.py +0 -0
  13. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/cli/points.py +0 -0
  14. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/cli/teach.py +0 -0
  15. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/config.py +0 -0
  16. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/connection.py +0 -0
  17. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/exceptions.py +0 -0
  18. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/geometry.py +0 -0
  19. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/gripper/__init__.py +0 -0
  20. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/gripper/base.py +0 -0
  21. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/gripper/digital.py +0 -0
  22. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/gripper/presets.py +0 -0
  23. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/gripper/robotiq.py +0 -0
  24. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/gripper/robotiq_preamble.py +0 -0
  25. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/io.py +0 -0
  26. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/points.py +0 -0
  27. {urkit-0.3.22 → urkit-0.3.23}/src/urkit/telemetry.py +0 -0
  28. {urkit-0.3.22 → urkit-0.3.23}/src/urkit.egg-info/SOURCES.txt +0 -0
  29. {urkit-0.3.22 → urkit-0.3.23}/src/urkit.egg-info/dependency_links.txt +0 -0
  30. {urkit-0.3.22 → urkit-0.3.23}/src/urkit.egg-info/entry_points.txt +0 -0
  31. {urkit-0.3.22 → urkit-0.3.23}/src/urkit.egg-info/requires.txt +0 -0
  32. {urkit-0.3.22 → urkit-0.3.23}/src/urkit.egg-info/top_level.txt +0 -0
  33. {urkit-0.3.22 → urkit-0.3.23}/tests/test_exceptions.py +0 -0
  34. {urkit-0.3.22 → urkit-0.3.23}/tests/test_geometry.py +0 -0
  35. {urkit-0.3.22 → urkit-0.3.23}/tests/test_gripper.py +0 -0
  36. {urkit-0.3.22 → urkit-0.3.23}/tests/test_gripper_factory.py +0 -0
  37. {urkit-0.3.22 → urkit-0.3.23}/tests/test_gripper_presets.py +0 -0
  38. {urkit-0.3.22 → urkit-0.3.23}/tests/test_move_sequence.py +0 -0
  39. {urkit-0.3.22 → urkit-0.3.23}/tests/test_points.py +0 -0
  40. {urkit-0.3.22 → urkit-0.3.23}/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.22
3
+ Version: 0.3.23
4
4
  Summary: Universal Robots e-Series control toolkit built on ur_rtde
5
5
  Author: URKit Contributors
6
6
  License: MIT
@@ -506,6 +506,16 @@ robot.move_until_contact(speed_z=-0.02, zero_first=False)
506
506
  # Full speed vector
507
507
  robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
508
508
 
509
+ # Abort after 10s or 200mm of travel, whichever comes first
510
+ robot.move_until_contact(speed_z=-0.02, timeout=10.0, max_distance=0.2)
511
+
512
+ # Returns True if contact detected, False if timeout/max_distance hit
513
+ for _ in range(3):
514
+ if robot.move_until_contact(speed_z=-0.02, timeout=10.0, max_distance=0.2):
515
+ break # contact detected
516
+ # No contact — back off and retry
517
+ robot.move_by(z=0.01)
518
+
509
519
  # Manual zero (e.g. before custom force-based logic)
510
520
  robot.zero_ft_sensor()
511
521
  ```
@@ -480,6 +480,16 @@ robot.move_until_contact(speed_z=-0.02, zero_first=False)
480
480
  # Full speed vector
481
481
  robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
482
482
 
483
+ # Abort after 10s or 200mm of travel, whichever comes first
484
+ robot.move_until_contact(speed_z=-0.02, timeout=10.0, max_distance=0.2)
485
+
486
+ # Returns True if contact detected, False if timeout/max_distance hit
487
+ for _ in range(3):
488
+ if robot.move_until_contact(speed_z=-0.02, timeout=10.0, max_distance=0.2):
489
+ break # contact detected
490
+ # No contact — back off and retry
491
+ robot.move_by(z=0.01)
492
+
483
493
  # Manual zero (e.g. before custom force-based logic)
484
494
  robot.zero_ft_sensor()
485
495
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "urkit"
7
- version = "0.3.22"
7
+ version = "0.3.23"
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.22"
27
+ __version__ = "0.3.23"
28
28
 
29
29
  from urkit.config import load_config, resolve_config
30
30
  from urkit.exceptions import (
@@ -9,6 +9,7 @@ from __future__ import annotations
9
9
 
10
10
  import io
11
11
  import logging
12
+ import math
12
13
  import os
13
14
  import sys
14
15
  import time
@@ -278,7 +279,9 @@ class Motion:
278
279
  threshold: float = 5.0,
279
280
  acceleration: float = 0.1,
280
281
  zero_first: bool = True,
281
- ) -> None:
282
+ timeout: float | None = None,
283
+ max_distance: float | None = None,
284
+ ) -> bool:
282
285
  """Move until contact is detected via TCP force sensing.
283
286
 
284
287
  Runs a 500 Hz control loop that sends ``speedL()`` commands and
@@ -300,15 +303,30 @@ class Motion:
300
303
  zero_first: If True (default), zero the FT sensor before reading
301
304
  the baseline. Set to False if you need absolute force values
302
305
  rather than delta from zero.
306
+ timeout: Maximum time in seconds before aborting. If exceeded,
307
+ returns ``False``. Set to ``None`` (default) for no time limit.
308
+ max_distance: Maximum TCP travel distance in meters before aborting.
309
+ If exceeded, returns ``False``. Set to ``None`` (default) for
310
+ no distance limit.
311
+
312
+ Returns:
313
+ ``True`` if contact was detected, ``False`` if timeout or
314
+ max_distance was reached without contact.
303
315
 
304
316
  Raises:
305
- MotionError: If the command fails or the vector is invalid.
317
+ MotionError: If the command fails (connection lost, E-stop,
318
+ protective stop, invalid params).
306
319
 
307
320
  Example:
308
321
  >>> # Move straight down until contact (zeros FT sensor first)
309
322
  >>> motion.move_until_contact([0, 0, -0.02, 0, 0, 0])
310
323
  >>> # Move down while rotating, higher threshold
311
324
  >>> motion.move_until_contact([0, 0, -0.02, 0, 0.1, 0], threshold=10.0)
325
+ >>> # Retry loop with guards
326
+ >>> for _ in range(3):
327
+ ... if motion.move_until_contact([0, 0, -0.02, 0, 0, 0], timeout=10.0, max_distance=0.2):
328
+ ... break
329
+ ... # No contact — reposition and retry
312
330
  """
313
331
  if len(speed_vector) != 6:
314
332
  raise MotionError(
@@ -316,11 +334,16 @@ class Motion:
316
334
  )
317
335
  if threshold <= 0:
318
336
  raise MotionError(f"Threshold must be > 0, got {threshold}.")
337
+ if timeout is not None and timeout <= 0:
338
+ raise MotionError(f"Timeout must be > 0, got {timeout}.")
339
+ if max_distance is not None and max_distance <= 0:
340
+ raise MotionError(f"max_distance must be > 0, got {max_distance}.")
319
341
 
342
+ contact_detected = False
320
343
  try:
321
344
  logger.debug(
322
- "move_until_contact: speed_vector=%s, threshold=%.2f",
323
- speed_vector, threshold,
345
+ "move_until_contact: speed_vector=%s, threshold=%.2f, timeout=%s, max_distance=%s",
346
+ speed_vector, threshold, timeout, max_distance,
324
347
  )
325
348
 
326
349
  # Zero FT sensor to clear gravity bias before reading baseline
@@ -331,7 +354,34 @@ class Motion:
331
354
  # Baseline force reading before the loop
332
355
  baseline = list(self._rtde_r.getActualTCPForce())
333
356
 
357
+ # Track timeout and distance guards
358
+ start_time = time.monotonic() if timeout is not None else None
359
+ start_pose = list(self._rtde_r.getActualTCPPose())[:3] if max_distance is not None else None
360
+
334
361
  while True:
362
+ # Check time timeout
363
+ if start_time is not None and timeout is not None:
364
+ elapsed = time.monotonic() - start_time
365
+ if elapsed >= timeout:
366
+ logger.info(
367
+ "move_until_contact timed out after %.1fs (limit: %.1fs)",
368
+ elapsed, timeout,
369
+ )
370
+ break
371
+
372
+ # Check distance timeout
373
+ if start_pose is not None and max_distance is not None:
374
+ current_pose = list(self._rtde_r.getActualTCPPose())[:3]
375
+ distance = math.sqrt(
376
+ sum((current_pose[i] - start_pose[i]) ** 2 for i in range(3))
377
+ )
378
+ if distance >= max_distance:
379
+ logger.info(
380
+ "move_until_contact exceeded max_distance: %.3fm travelled (limit: %.3fm)",
381
+ distance, max_distance,
382
+ )
383
+ break
384
+
335
385
  if not self._rtde_c.isConnected():
336
386
  raise MotionError(
337
387
  "RTDE connection lost during move_until_contact. "
@@ -363,6 +413,7 @@ class Motion:
363
413
  if any(
364
414
  abs(current[i] - baseline[i]) > threshold for i in range(6)
365
415
  ):
416
+ contact_detected = True
366
417
  break
367
418
 
368
419
  with _suppress_rtde_stderr():
@@ -378,6 +429,8 @@ class Motion:
378
429
  except Exception:
379
430
  pass
380
431
 
432
+ return contact_detected
433
+
381
434
  def move_velocity(
382
435
  self,
383
436
  speed_vector: list[float],
@@ -1614,13 +1614,15 @@ class URRobot:
1614
1614
  threshold: float = 5.0,
1615
1615
  acceleration: float = 0.1,
1616
1616
  zero_first: bool = True,
1617
+ timeout: float | None = None,
1618
+ max_distance: float | None = None,
1617
1619
  speed_x: float = 0.0,
1618
1620
  speed_y: float = 0.0,
1619
1621
  speed_z: float = 0.0,
1620
1622
  speed_rx: float = 0.0,
1621
1623
  speed_ry: float = 0.0,
1622
1624
  speed_rz: float = 0.0,
1623
- ) -> None:
1625
+ ) -> bool:
1624
1626
  """Move until contact is detected via TCP force sensing.
1625
1627
 
1626
1628
  Runs an interruptible control loop — press Ctrl+C to stop at any time.
@@ -1636,9 +1638,17 @@ class URRobot:
1636
1638
  acceleration: Acceleration limit passed to ``speedL()`` in m/s².
1637
1639
  zero_first: If True (default), zero the FT sensor before reading
1638
1640
  the baseline. Set to False if you need absolute force values.
1641
+ timeout: Maximum time in seconds before aborting. Set to ``None``
1642
+ (default) for no time limit.
1643
+ max_distance: Maximum TCP travel distance in meters before aborting.
1644
+ Set to ``None`` (default) for no distance limit.
1639
1645
  speed_x, speed_y, speed_z: Linear speed components in m/s.
1640
1646
  speed_rx, speed_ry, speed_rz: Angular speed components in rad/s.
1641
1647
 
1648
+ Returns:
1649
+ ``True`` if contact was detected, ``False`` if timeout or
1650
+ max_distance was reached without contact.
1651
+
1642
1652
  Example:
1643
1653
  >>> # Move straight down until contact
1644
1654
  >>> robot.move_until_contact(speed_z=-0.02)
@@ -1646,6 +1656,8 @@ class URRobot:
1646
1656
  >>> robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
1647
1657
  >>> # Higher threshold for heavier contact
1648
1658
  >>> robot.move_until_contact(speed_z=-0.02, threshold=10.0)
1659
+ >>> # Abort after 10s or 200mm, whichever comes first
1660
+ >>> robot.move_until_contact(speed_z=-0.02, timeout=10.0, max_distance=0.2)
1649
1661
  """
1650
1662
  self._check_connection()
1651
1663
  self._disable_freedrive_guard()
@@ -1655,11 +1667,13 @@ class URRobot:
1655
1667
  else:
1656
1668
  final_vector = [speed_x, speed_y, speed_z, speed_rx, speed_ry, speed_rz]
1657
1669
 
1658
- self._motion.move_until_contact(
1670
+ return self._motion.move_until_contact(
1659
1671
  final_vector,
1660
1672
  threshold=threshold,
1661
1673
  acceleration=acceleration,
1662
1674
  zero_first=zero_first,
1675
+ timeout=timeout,
1676
+ max_distance=max_distance,
1663
1677
  )
1664
1678
 
1665
1679
  def move_velocity(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: urkit
3
- Version: 0.3.22
3
+ Version: 0.3.23
4
4
  Summary: Universal Robots e-Series control toolkit built on ur_rtde
5
5
  Author: URKit Contributors
6
6
  License: MIT
@@ -506,6 +506,16 @@ robot.move_until_contact(speed_z=-0.02, zero_first=False)
506
506
  # Full speed vector
507
507
  robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
508
508
 
509
+ # Abort after 10s or 200mm of travel, whichever comes first
510
+ robot.move_until_contact(speed_z=-0.02, timeout=10.0, max_distance=0.2)
511
+
512
+ # Returns True if contact detected, False if timeout/max_distance hit
513
+ for _ in range(3):
514
+ if robot.move_until_contact(speed_z=-0.02, timeout=10.0, max_distance=0.2):
515
+ break # contact detected
516
+ # No contact — back off and retry
517
+ robot.move_by(z=0.01)
518
+
509
519
  # Manual zero (e.g. before custom force-based logic)
510
520
  robot.zero_ft_sensor()
511
521
  ```
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