urkit 0.3.6__tar.gz → 0.3.8__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.6 → urkit-0.3.8}/PKG-INFO +20 -7
- {urkit-0.3.6 → urkit-0.3.8}/README.md +19 -6
- {urkit-0.3.6 → urkit-0.3.8}/pyproject.toml +1 -1
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/__init__.py +1 -1
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/cli/teach.py +1 -1
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/motion.py +28 -4
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/robot.py +137 -28
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit.egg-info/PKG-INFO +20 -7
- {urkit-0.3.6 → urkit-0.3.8}/setup.cfg +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/__main__.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/cli/__init__.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/cli/colors.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/cli/connection_monitor.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/cli/points.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/config.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/connection.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/exceptions.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/geometry.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/gripper/__init__.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/gripper/base.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/gripper/digital.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/gripper/presets.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/gripper/robotiq.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/gripper/robotiq_preamble.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/io.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/points.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit/telemetry.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit.egg-info/SOURCES.txt +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit.egg-info/dependency_links.txt +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit.egg-info/entry_points.txt +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit.egg-info/requires.txt +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/src/urkit.egg-info/top_level.txt +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/tests/test_exceptions.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/tests/test_geometry.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/tests/test_gripper.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/tests/test_gripper_factory.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/tests/test_gripper_presets.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/tests/test_points.py +0 -0
- {urkit-0.3.6 → urkit-0.3.8}/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.
|
|
3
|
+
Version: 0.3.8
|
|
4
4
|
Summary: Universal Robots e-Series control toolkit built on ur_rtde
|
|
5
5
|
Author: URKit Contributors
|
|
6
6
|
License: MIT
|
|
@@ -88,7 +88,7 @@ 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")
|
|
91
|
-
robot.move_to("pick",
|
|
91
|
+
robot.move_to("pick", offset_z=0.05) # 5cm above
|
|
92
92
|
robot.gripper.close()
|
|
93
93
|
robot.move_to("place")
|
|
94
94
|
robot.gripper.open()
|
|
@@ -394,10 +394,12 @@ A pose is `[x, y, z, rx, ry, rz]`: position in meters and orientation as a **rot
|
|
|
394
394
|
|
|
395
395
|
#### Offsets
|
|
396
396
|
|
|
397
|
-
Offsets
|
|
397
|
+
Offsets can use individual parameters or a full 6-element list:
|
|
398
398
|
|
|
399
399
|
```python
|
|
400
|
-
robot.move_to("pick",
|
|
400
|
+
robot.move_to("pick", offset_z=0.05) # 5cm above
|
|
401
|
+
robot.move_to("pick", offset_x=0.01, offset_z=-0.02) # combined
|
|
402
|
+
robot.move_to("pick", offset=[0, 0, 0.05, 0, 0.1, 0]) # full with rotation
|
|
401
403
|
```
|
|
402
404
|
|
|
403
405
|
#### Resolve a Pose
|
|
@@ -416,7 +418,7 @@ robot.move_to(pose) # move to the resolved pose later
|
|
|
416
418
|
from urkit import MoveFrame
|
|
417
419
|
|
|
418
420
|
robot.move_frame = MoveFrame.TOOL # default is BASE
|
|
419
|
-
robot.move_relative(
|
|
421
|
+
robot.move_relative(delta_z=0.05) # 5cm along tool Z
|
|
420
422
|
```
|
|
421
423
|
|
|
422
424
|
- **BASE** (default): delta relative to robot base
|
|
@@ -440,8 +442,9 @@ robot.import_points("backup.json")
|
|
|
440
442
|
#### Relative Moves
|
|
441
443
|
|
|
442
444
|
```python
|
|
443
|
-
robot.move_relative(
|
|
444
|
-
robot.move_relative(
|
|
445
|
+
robot.move_relative(delta_y=0.01) # 1cm along Y
|
|
446
|
+
robot.move_relative(delta_z=0.05, frame=MoveFrame.TOOL) # 5cm along tool Z
|
|
447
|
+
robot.move_relative([0, 0.01, 0, 0, 0, 0])
|
|
445
448
|
```
|
|
446
449
|
|
|
447
450
|
#### Sequences with Blending
|
|
@@ -454,7 +457,17 @@ robot.move_sequence(["a", "b", "c"], blend_radius=0.02)
|
|
|
454
457
|
#### Contact Detection
|
|
455
458
|
|
|
456
459
|
```python
|
|
460
|
+
# Zeros FT sensor automatically, then moves until force exceeds threshold
|
|
457
461
|
robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
|
|
462
|
+
|
|
463
|
+
# Custom threshold (default: 5.0 N/Nm)
|
|
464
|
+
robot.move_until_contact([0, 0, -0.02, 0, 0, 0], threshold=10.0)
|
|
465
|
+
|
|
466
|
+
# Skip zeroing if you need absolute force values
|
|
467
|
+
robot.move_until_contact([0, 0, -0.02, 0, 0, 0], zero_first=False)
|
|
468
|
+
|
|
469
|
+
# Manual zero (e.g. before custom force-based logic)
|
|
470
|
+
robot.zero_ft_sensor()
|
|
458
471
|
```
|
|
459
472
|
|
|
460
473
|
#### Velocity Control
|
|
@@ -62,7 +62,7 @@ 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")
|
|
65
|
-
robot.move_to("pick",
|
|
65
|
+
robot.move_to("pick", offset_z=0.05) # 5cm above
|
|
66
66
|
robot.gripper.close()
|
|
67
67
|
robot.move_to("place")
|
|
68
68
|
robot.gripper.open()
|
|
@@ -368,10 +368,12 @@ A pose is `[x, y, z, rx, ry, rz]`: position in meters and orientation as a **rot
|
|
|
368
368
|
|
|
369
369
|
#### Offsets
|
|
370
370
|
|
|
371
|
-
Offsets
|
|
371
|
+
Offsets can use individual parameters or a full 6-element list:
|
|
372
372
|
|
|
373
373
|
```python
|
|
374
|
-
robot.move_to("pick",
|
|
374
|
+
robot.move_to("pick", offset_z=0.05) # 5cm above
|
|
375
|
+
robot.move_to("pick", offset_x=0.01, offset_z=-0.02) # combined
|
|
376
|
+
robot.move_to("pick", offset=[0, 0, 0.05, 0, 0.1, 0]) # full with rotation
|
|
375
377
|
```
|
|
376
378
|
|
|
377
379
|
#### Resolve a Pose
|
|
@@ -390,7 +392,7 @@ robot.move_to(pose) # move to the resolved pose later
|
|
|
390
392
|
from urkit import MoveFrame
|
|
391
393
|
|
|
392
394
|
robot.move_frame = MoveFrame.TOOL # default is BASE
|
|
393
|
-
robot.move_relative(
|
|
395
|
+
robot.move_relative(delta_z=0.05) # 5cm along tool Z
|
|
394
396
|
```
|
|
395
397
|
|
|
396
398
|
- **BASE** (default): delta relative to robot base
|
|
@@ -414,8 +416,9 @@ robot.import_points("backup.json")
|
|
|
414
416
|
#### Relative Moves
|
|
415
417
|
|
|
416
418
|
```python
|
|
417
|
-
robot.move_relative(
|
|
418
|
-
robot.move_relative(
|
|
419
|
+
robot.move_relative(delta_y=0.01) # 1cm along Y
|
|
420
|
+
robot.move_relative(delta_z=0.05, frame=MoveFrame.TOOL) # 5cm along tool Z
|
|
421
|
+
robot.move_relative([0, 0.01, 0, 0, 0, 0])
|
|
419
422
|
```
|
|
420
423
|
|
|
421
424
|
#### Sequences with Blending
|
|
@@ -428,7 +431,17 @@ robot.move_sequence(["a", "b", "c"], blend_radius=0.02)
|
|
|
428
431
|
#### Contact Detection
|
|
429
432
|
|
|
430
433
|
```python
|
|
434
|
+
# Zeros FT sensor automatically, then moves until force exceeds threshold
|
|
431
435
|
robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
|
|
436
|
+
|
|
437
|
+
# Custom threshold (default: 5.0 N/Nm)
|
|
438
|
+
robot.move_until_contact([0, 0, -0.02, 0, 0, 0], threshold=10.0)
|
|
439
|
+
|
|
440
|
+
# Skip zeroing if you need absolute force values
|
|
441
|
+
robot.move_until_contact([0, 0, -0.02, 0, 0, 0], zero_first=False)
|
|
442
|
+
|
|
443
|
+
# Manual zero (e.g. before custom force-based logic)
|
|
444
|
+
robot.zero_ft_sensor()
|
|
432
445
|
```
|
|
433
446
|
|
|
434
447
|
#### Velocity Control
|
|
@@ -1441,7 +1441,7 @@ def teach_command(args) -> None:
|
|
|
1441
1441
|
**gripper_kwargs,
|
|
1442
1442
|
)
|
|
1443
1443
|
print(" Connected.", flush=True)
|
|
1444
|
-
if robot.
|
|
1444
|
+
if robot._activate_gripper():
|
|
1445
1445
|
print(" Gripper activated.", flush=True)
|
|
1446
1446
|
except ConnectionError as e:
|
|
1447
1447
|
print(f"Connection error: {e}")
|
|
@@ -10,7 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
import logging
|
|
11
11
|
import os
|
|
12
12
|
import sys
|
|
13
|
-
import time
|
|
13
|
+
import time
|
|
14
14
|
from contextlib import contextmanager
|
|
15
15
|
from enum import IntEnum
|
|
16
16
|
from typing import Iterator
|
|
@@ -255,12 +255,28 @@ class Motion:
|
|
|
255
255
|
f"Relative move failed: {e}"
|
|
256
256
|
)
|
|
257
257
|
|
|
258
|
+
def zero_ft_sensor(self) -> None:
|
|
259
|
+
"""Zero the robot's force/torque sensor.
|
|
260
|
+
|
|
261
|
+
Clears the baseline so that subsequent ``getActualTCPForce()``
|
|
262
|
+
readings reflect only forces applied after this call. Call before
|
|
263
|
+
``move_until_contact()`` or any force-based operation.
|
|
264
|
+
|
|
265
|
+
Raises:
|
|
266
|
+
MotionError: If the command fails.
|
|
267
|
+
"""
|
|
268
|
+
try:
|
|
269
|
+
self._rtde_c.zeroFtSensor()
|
|
270
|
+
except Exception as e:
|
|
271
|
+
raise MotionError(f"Failed to zero FT sensor: {e}")
|
|
272
|
+
|
|
258
273
|
def move_until_contact(
|
|
259
274
|
self,
|
|
260
275
|
speed_vector: list[float],
|
|
261
276
|
*,
|
|
262
277
|
threshold: float = 5.0,
|
|
263
278
|
acceleration: float = 0.1,
|
|
279
|
+
zero_first: bool = True,
|
|
264
280
|
) -> None:
|
|
265
281
|
"""Move until contact is detected via TCP force sensing.
|
|
266
282
|
|
|
@@ -280,12 +296,15 @@ class Motion:
|
|
|
280
296
|
Contact fires when any of the 6 wrench components changes
|
|
281
297
|
by more than this value from the baseline reading.
|
|
282
298
|
acceleration: Acceleration limit passed to ``speedL()`` in m/s².
|
|
299
|
+
zero_first: If True (default), zero the FT sensor before reading
|
|
300
|
+
the baseline. Set to False if you need absolute force values
|
|
301
|
+
rather than delta from zero.
|
|
283
302
|
|
|
284
303
|
Raises:
|
|
285
304
|
MotionError: If the command fails or the vector is invalid.
|
|
286
305
|
|
|
287
306
|
Example:
|
|
288
|
-
>>> # Move straight down until contact
|
|
307
|
+
>>> # Move straight down until contact (zeros FT sensor first)
|
|
289
308
|
>>> motion.move_until_contact([0, 0, -0.02, 0, 0, 0])
|
|
290
309
|
>>> # Move down while rotating, higher threshold
|
|
291
310
|
>>> motion.move_until_contact([0, 0, -0.02, 0, 0.1, 0], threshold=10.0)
|
|
@@ -303,6 +322,11 @@ class Motion:
|
|
|
303
322
|
speed_vector, threshold,
|
|
304
323
|
)
|
|
305
324
|
|
|
325
|
+
# Zero FT sensor to clear gravity bias before reading baseline
|
|
326
|
+
if zero_first:
|
|
327
|
+
self.zero_ft_sensor()
|
|
328
|
+
time.sleep(0.05) # let sensor settle after zero
|
|
329
|
+
|
|
306
330
|
# Baseline force reading before the loop
|
|
307
331
|
baseline = list(self._rtde_r.getActualTCPForce())
|
|
308
332
|
|
|
@@ -387,9 +411,9 @@ class Motion:
|
|
|
387
411
|
raise MotionError(f"Duration must be > 0, got {duration}.")
|
|
388
412
|
|
|
389
413
|
try:
|
|
390
|
-
start =
|
|
414
|
+
start = time.monotonic()
|
|
391
415
|
while True:
|
|
392
|
-
elapsed =
|
|
416
|
+
elapsed = time.monotonic() - start
|
|
393
417
|
if elapsed >= duration:
|
|
394
418
|
break
|
|
395
419
|
if not self._rtde_c.isConnected():
|
|
@@ -254,19 +254,19 @@ class URRobot:
|
|
|
254
254
|
|
|
255
255
|
logger.info("URRobot initialized at %s", ip)
|
|
256
256
|
|
|
257
|
-
def
|
|
258
|
-
"""Activate the gripper with
|
|
257
|
+
def _activate_gripper(self, *, timeout: float = 10.0) -> bool:
|
|
258
|
+
"""Activate the gripper with error handling (internal use).
|
|
259
259
|
|
|
260
260
|
Tries to activate the configured gripper. If activation fails
|
|
261
261
|
or times out (e.g., gripper not physically connected), disconnects
|
|
262
262
|
the gripper, nulls out ``self._gripper``, and returns ``False``.
|
|
263
263
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
264
|
+
Users should call ``robot.gripper.activate()`` directly instead.
|
|
265
|
+
This method exists for the CLI which needs graceful fallback
|
|
266
|
+
when the gripper isn't connected.
|
|
267
267
|
|
|
268
268
|
Args:
|
|
269
|
-
timeout: Maximum seconds to wait for activation (default
|
|
269
|
+
timeout: Maximum seconds to wait for activation (default 10.0).
|
|
270
270
|
|
|
271
271
|
Returns:
|
|
272
272
|
``True`` if the gripper was activated successfully,
|
|
@@ -438,7 +438,7 @@ class URRobot:
|
|
|
438
438
|
Example:
|
|
439
439
|
>>> robot.move_frame = MoveFrame.TOOL
|
|
440
440
|
>>> robot.move_relative([0.01, 0, 0, 0, 0, 0]) # +X in tool frame
|
|
441
|
-
>>> robot.move_to("pick",
|
|
441
|
+
>>> robot.move_to("pick", offset_z=0.05) # offset in tool frame
|
|
442
442
|
"""
|
|
443
443
|
return self._move_frame
|
|
444
444
|
|
|
@@ -821,6 +821,12 @@ class URRobot:
|
|
|
821
821
|
*,
|
|
822
822
|
offset: list[float] | None = None,
|
|
823
823
|
frame: MoveFrame | None = None,
|
|
824
|
+
offset_x: float = 0.0,
|
|
825
|
+
offset_y: float = 0.0,
|
|
826
|
+
offset_z: float = 0.0,
|
|
827
|
+
offset_rx: float = 0.0,
|
|
828
|
+
offset_ry: float = 0.0,
|
|
829
|
+
offset_rz: float = 0.0,
|
|
824
830
|
) -> list[float]:
|
|
825
831
|
"""Resolve a saved point or raw pose to a TCP pose.
|
|
826
832
|
|
|
@@ -831,9 +837,16 @@ class URRobot:
|
|
|
831
837
|
target: A saved point name (str) or a raw TCP pose
|
|
832
838
|
[x, y, z, rx, ry, rz].
|
|
833
839
|
offset: Optional offset [dx, dy, dz, drx, dry, drz]
|
|
834
|
-
applied to the target pose.
|
|
840
|
+
applied to the target pose. Mutually exclusive with
|
|
841
|
+
individual offset_* parameters.
|
|
835
842
|
frame: Coordinate frame for the offset. Falls back to the
|
|
836
843
|
current ``move_frame`` property (BASE or TOOL).
|
|
844
|
+
offset_x: X offset in meters (default 0.0).
|
|
845
|
+
offset_y: Y offset in meters (default 0.0).
|
|
846
|
+
offset_z: Z offset in meters (default 0.0).
|
|
847
|
+
offset_rx: Roll offset in radians (default 0.0).
|
|
848
|
+
offset_ry: Pitch offset in radians (default 0.0).
|
|
849
|
+
offset_rz: Yaw offset in radians (default 0.0).
|
|
837
850
|
|
|
838
851
|
Returns:
|
|
839
852
|
TCP pose as [x, y, z, rx, ry, rz].
|
|
@@ -843,18 +856,32 @@ class URRobot:
|
|
|
843
856
|
|
|
844
857
|
Example:
|
|
845
858
|
>>> pose = robot.get_pose("pick")
|
|
846
|
-
>>> pose = robot.get_pose("pick",
|
|
859
|
+
>>> pose = robot.get_pose("pick", offset_z=0.05)
|
|
847
860
|
>>> robot.move_to(pose)
|
|
848
861
|
"""
|
|
849
862
|
point = self._lookup_point(target)
|
|
850
863
|
|
|
851
|
-
|
|
852
|
-
|
|
864
|
+
# Build offset from individual params or use provided list
|
|
865
|
+
individual_offset = [offset_x, offset_y, offset_z, offset_rx, offset_ry, offset_rz]
|
|
866
|
+
has_individual = any(v != 0.0 for v in individual_offset)
|
|
867
|
+
|
|
868
|
+
if offset is not None and has_individual:
|
|
869
|
+
raise PointError(
|
|
870
|
+
"Cannot use both 'offset' list and individual offset_* parameters. "
|
|
871
|
+
"Use one or the other."
|
|
872
|
+
)
|
|
873
|
+
|
|
874
|
+
final_offset = offset if offset is not None and not has_individual else (
|
|
875
|
+
individual_offset if has_individual else None
|
|
876
|
+
)
|
|
877
|
+
|
|
878
|
+
if final_offset is not None:
|
|
879
|
+
if len(final_offset) != 6:
|
|
853
880
|
raise PointError(
|
|
854
881
|
f"Offset must have 6 values [dx, dy, dz, drx, dry, drz], "
|
|
855
|
-
f"got {len(
|
|
882
|
+
f"got {len(final_offset)}."
|
|
856
883
|
)
|
|
857
|
-
point = point.with_offset(
|
|
884
|
+
point = point.with_offset(final_offset, frame=frame or self._move_frame)
|
|
858
885
|
|
|
859
886
|
return list(point.pose)
|
|
860
887
|
|
|
@@ -868,6 +895,12 @@ class URRobot:
|
|
|
868
895
|
vel: float | None = None,
|
|
869
896
|
acc: float | None = None,
|
|
870
897
|
asynchronous: bool = False,
|
|
898
|
+
offset_x: float = 0.0,
|
|
899
|
+
offset_y: float = 0.0,
|
|
900
|
+
offset_z: float = 0.0,
|
|
901
|
+
offset_rx: float = 0.0,
|
|
902
|
+
offset_ry: float = 0.0,
|
|
903
|
+
offset_rz: float = 0.0,
|
|
871
904
|
) -> None:
|
|
872
905
|
"""Move to a saved point or raw pose.
|
|
873
906
|
|
|
@@ -877,11 +910,18 @@ class URRobot:
|
|
|
877
910
|
linear: If True (default), use Cartesian linear move (moveL).
|
|
878
911
|
If False, use joint-space move (moveJ).
|
|
879
912
|
offset: Optional offset [dx, dy, dz, drx, dry, drz]
|
|
880
|
-
applied to the target pose before moving.
|
|
913
|
+
applied to the target pose before moving. Mutually
|
|
914
|
+
exclusive with individual offset_* parameters.
|
|
881
915
|
frame: Coordinate frame for the offset. Falls back to the
|
|
882
916
|
current ``move_frame`` property (BASE or TOOL).
|
|
883
917
|
vel: Velocity override. Falls back to default_vel.
|
|
884
918
|
acc: Acceleration override. Falls back to default_acc.
|
|
919
|
+
offset_x: X offset in meters (default 0.0).
|
|
920
|
+
offset_y: Y offset in meters (default 0.0).
|
|
921
|
+
offset_z: Z offset in meters (default 0.0).
|
|
922
|
+
offset_rx: Roll offset in radians (default 0.0).
|
|
923
|
+
offset_ry: Pitch offset in radians (default 0.0).
|
|
924
|
+
offset_rz: Yaw offset in radians (default 0.0).
|
|
885
925
|
|
|
886
926
|
Raises:
|
|
887
927
|
MotionError: If the move fails or IK has no solution.
|
|
@@ -890,8 +930,9 @@ class URRobot:
|
|
|
890
930
|
Example:
|
|
891
931
|
>>> robot.move_to("home")
|
|
892
932
|
>>> robot.move_to("pick", linear=False)
|
|
893
|
-
>>> robot.move_to("
|
|
894
|
-
>>> robot.move_to("
|
|
933
|
+
>>> robot.move_to("pick", offset_z=0.05) # 5cm above
|
|
934
|
+
>>> robot.move_to("pick", offset_x=0.01, offset_z=-0.02)
|
|
935
|
+
>>> robot.move_to("pick", offset=[0, 0, 0.05, 0, 0.1, 0]) # full
|
|
895
936
|
>>> robot.move_to([0.5, 0, 0.3, 0, 0, 0]) # raw pose
|
|
896
937
|
"""
|
|
897
938
|
self._check_connection()
|
|
@@ -899,14 +940,28 @@ class URRobot:
|
|
|
899
940
|
|
|
900
941
|
point = self._lookup_point(target)
|
|
901
942
|
|
|
943
|
+
# Build offset from individual params or use provided list
|
|
944
|
+
individual_offset = [offset_x, offset_y, offset_z, offset_rx, offset_ry, offset_rz]
|
|
945
|
+
has_individual = any(v != 0.0 for v in individual_offset)
|
|
946
|
+
|
|
947
|
+
if offset is not None and has_individual:
|
|
948
|
+
raise PointError(
|
|
949
|
+
"Cannot use both 'offset' list and individual offset_* parameters. "
|
|
950
|
+
"Use one or the other."
|
|
951
|
+
)
|
|
952
|
+
|
|
953
|
+
final_offset = offset if offset is not None and not has_individual else (
|
|
954
|
+
individual_offset if has_individual else None
|
|
955
|
+
)
|
|
956
|
+
|
|
902
957
|
# Apply offset if provided
|
|
903
|
-
if
|
|
904
|
-
if len(
|
|
958
|
+
if final_offset is not None:
|
|
959
|
+
if len(final_offset) != 6:
|
|
905
960
|
raise PointError(
|
|
906
961
|
f"Offset must have 6 values [dx, dy, dz, drx, dry, drz], "
|
|
907
|
-
f"got {len(
|
|
962
|
+
f"got {len(final_offset)}."
|
|
908
963
|
)
|
|
909
|
-
point = point.with_offset(
|
|
964
|
+
point = point.with_offset(final_offset, frame=frame or self._move_frame)
|
|
910
965
|
|
|
911
966
|
pose = list(point.pose)
|
|
912
967
|
|
|
@@ -978,12 +1033,18 @@ class URRobot:
|
|
|
978
1033
|
|
|
979
1034
|
def move_relative(
|
|
980
1035
|
self,
|
|
981
|
-
delta: list[float],
|
|
1036
|
+
delta: list[float] | None = None,
|
|
982
1037
|
*,
|
|
983
1038
|
linear: bool = True,
|
|
984
1039
|
frame: MoveFrame | None = None,
|
|
985
1040
|
vel: float | None = None,
|
|
986
1041
|
acc: float | None = None,
|
|
1042
|
+
delta_x: float = 0.0,
|
|
1043
|
+
delta_y: float = 0.0,
|
|
1044
|
+
delta_z: float = 0.0,
|
|
1045
|
+
delta_rx: float = 0.0,
|
|
1046
|
+
delta_ry: float = 0.0,
|
|
1047
|
+
delta_rz: float = 0.0,
|
|
987
1048
|
) -> None:
|
|
988
1049
|
"""Relative Cartesian move from the current position.
|
|
989
1050
|
|
|
@@ -992,27 +1053,55 @@ class URRobot:
|
|
|
992
1053
|
|
|
993
1054
|
Args:
|
|
994
1055
|
delta: [dx, dy, dz, drx, dry, drz] in meters/radians.
|
|
1056
|
+
Mutually exclusive with individual delta_* parameters.
|
|
995
1057
|
linear: If True (default), use Cartesian linear move.
|
|
996
1058
|
If False, solve IK and use joint-space move.
|
|
997
1059
|
frame: Coordinate frame for the delta. Falls back to the
|
|
998
1060
|
current ``move_frame`` property (BASE or TOOL).
|
|
999
1061
|
vel: Velocity override. Falls back to default_vel.
|
|
1000
1062
|
acc: Acceleration override. Falls back to default_acc.
|
|
1063
|
+
delta_x: X delta in meters (default 0.0).
|
|
1064
|
+
delta_y: Y delta in meters (default 0.0).
|
|
1065
|
+
delta_z: Z delta in meters (default 0.0).
|
|
1066
|
+
delta_rx: Roll delta in radians (default 0.0).
|
|
1067
|
+
delta_ry: Pitch delta in radians (default 0.0).
|
|
1068
|
+
delta_rz: Yaw delta in radians (default 0.0).
|
|
1001
1069
|
|
|
1002
1070
|
Raises:
|
|
1003
1071
|
MotionError: If the move fails.
|
|
1004
1072
|
|
|
1005
1073
|
Example:
|
|
1006
|
-
>>> robot.move_relative(
|
|
1007
|
-
>>> robot.move_relative(
|
|
1074
|
+
>>> robot.move_relative(delta_y=0.01) # 1cm along Y
|
|
1075
|
+
>>> robot.move_relative(delta_z=0.05, frame=MoveFrame.TOOL)
|
|
1076
|
+
>>> robot.move_relative([0, 0.01, 0, 0, 0, 0]) # full list
|
|
1008
1077
|
"""
|
|
1009
1078
|
self._check_connection()
|
|
1010
1079
|
self._disable_freedrive_guard()
|
|
1011
1080
|
|
|
1012
|
-
|
|
1081
|
+
# Build delta from individual params or use provided list
|
|
1082
|
+
individual_delta = [delta_x, delta_y, delta_z, delta_rx, delta_ry, delta_rz]
|
|
1083
|
+
has_individual = any(v != 0.0 for v in individual_delta)
|
|
1084
|
+
|
|
1085
|
+
if delta is not None and has_individual:
|
|
1086
|
+
raise MotionError(
|
|
1087
|
+
"Cannot use both 'delta' list and individual delta_* parameters. "
|
|
1088
|
+
"Use one or the other."
|
|
1089
|
+
)
|
|
1090
|
+
|
|
1091
|
+
final_delta = delta if delta is not None and not has_individual else (
|
|
1092
|
+
individual_delta if has_individual else None
|
|
1093
|
+
)
|
|
1094
|
+
|
|
1095
|
+
if final_delta is None:
|
|
1096
|
+
raise MotionError(
|
|
1097
|
+
"Relative move requires either 'delta' list or at least one "
|
|
1098
|
+
"delta_* parameter."
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1101
|
+
if len(final_delta) != 6:
|
|
1013
1102
|
raise MotionError(
|
|
1014
1103
|
f"Relative move requires 6 values [dx,dy,dz,drx,dry,drz], "
|
|
1015
|
-
f"got {len(
|
|
1104
|
+
f"got {len(final_delta)}."
|
|
1016
1105
|
)
|
|
1017
1106
|
|
|
1018
1107
|
vel = vel if vel is not None else self._default_vel
|
|
@@ -1021,7 +1110,7 @@ class URRobot:
|
|
|
1021
1110
|
|
|
1022
1111
|
try:
|
|
1023
1112
|
current = list(self._rtde_r.getActualTCPPose())
|
|
1024
|
-
target = transform_pose_delta(current,
|
|
1113
|
+
target = transform_pose_delta(current, final_delta, effective_frame)
|
|
1025
1114
|
|
|
1026
1115
|
if linear:
|
|
1027
1116
|
self._motion.movel(target, vel=vel, acc=acc)
|
|
@@ -1113,16 +1202,31 @@ class URRobot:
|
|
|
1113
1202
|
except Exception as e:
|
|
1114
1203
|
raise MotionError(f"move_sequence failed: {e}")
|
|
1115
1204
|
|
|
1205
|
+
def zero_ft_sensor(self) -> None:
|
|
1206
|
+
"""Zero the robot's force/torque sensor.
|
|
1207
|
+
|
|
1208
|
+
Clears the baseline so that subsequent force/torque readings
|
|
1209
|
+
reflect only forces applied after this call. Call before
|
|
1210
|
+
``move_until_contact()`` or any force-based operation.
|
|
1211
|
+
|
|
1212
|
+
Raises:
|
|
1213
|
+
MotionError: If the command fails.
|
|
1214
|
+
"""
|
|
1215
|
+
self._check_connection()
|
|
1216
|
+
self._motion.zero_ft_sensor()
|
|
1217
|
+
|
|
1116
1218
|
def move_until_contact(
|
|
1117
1219
|
self,
|
|
1118
1220
|
speed_vector: list[float],
|
|
1119
1221
|
*,
|
|
1120
1222
|
threshold: float = 5.0,
|
|
1121
1223
|
acceleration: float = 0.1,
|
|
1224
|
+
zero_first: bool = True,
|
|
1122
1225
|
) -> None:
|
|
1123
1226
|
"""Move until contact is detected via TCP force sensing.
|
|
1124
1227
|
|
|
1125
1228
|
Runs an interruptible control loop — press Ctrl+C to stop at any time.
|
|
1229
|
+
Zeros the FT sensor by default before reading the baseline.
|
|
1126
1230
|
|
|
1127
1231
|
Args:
|
|
1128
1232
|
speed_vector: 6-element speed vector
|
|
@@ -1131,9 +1235,11 @@ class URRobot:
|
|
|
1131
1235
|
Contact fires when any wrench component changes by more
|
|
1132
1236
|
than this value from the baseline reading.
|
|
1133
1237
|
acceleration: Acceleration limit passed to ``speedL()`` in m/s².
|
|
1238
|
+
zero_first: If True (default), zero the FT sensor before reading
|
|
1239
|
+
the baseline. Set to False if you need absolute force values.
|
|
1134
1240
|
|
|
1135
1241
|
Example:
|
|
1136
|
-
>>> # Move straight down until contact
|
|
1242
|
+
>>> # Move straight down until contact (zeros FT sensor first)
|
|
1137
1243
|
>>> robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
|
|
1138
1244
|
>>> # Higher threshold for heavier contact
|
|
1139
1245
|
>>> robot.move_until_contact([0, 0, -0.02, 0, 0, 0], threshold=10.0)
|
|
@@ -1141,7 +1247,10 @@ class URRobot:
|
|
|
1141
1247
|
self._check_connection()
|
|
1142
1248
|
self._disable_freedrive_guard()
|
|
1143
1249
|
self._motion.move_until_contact(
|
|
1144
|
-
speed_vector,
|
|
1250
|
+
speed_vector,
|
|
1251
|
+
threshold=threshold,
|
|
1252
|
+
acceleration=acceleration,
|
|
1253
|
+
zero_first=zero_first,
|
|
1145
1254
|
)
|
|
1146
1255
|
|
|
1147
1256
|
def move_velocity(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: urkit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.8
|
|
4
4
|
Summary: Universal Robots e-Series control toolkit built on ur_rtde
|
|
5
5
|
Author: URKit Contributors
|
|
6
6
|
License: MIT
|
|
@@ -88,7 +88,7 @@ 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")
|
|
91
|
-
robot.move_to("pick",
|
|
91
|
+
robot.move_to("pick", offset_z=0.05) # 5cm above
|
|
92
92
|
robot.gripper.close()
|
|
93
93
|
robot.move_to("place")
|
|
94
94
|
robot.gripper.open()
|
|
@@ -394,10 +394,12 @@ A pose is `[x, y, z, rx, ry, rz]`: position in meters and orientation as a **rot
|
|
|
394
394
|
|
|
395
395
|
#### Offsets
|
|
396
396
|
|
|
397
|
-
Offsets
|
|
397
|
+
Offsets can use individual parameters or a full 6-element list:
|
|
398
398
|
|
|
399
399
|
```python
|
|
400
|
-
robot.move_to("pick",
|
|
400
|
+
robot.move_to("pick", offset_z=0.05) # 5cm above
|
|
401
|
+
robot.move_to("pick", offset_x=0.01, offset_z=-0.02) # combined
|
|
402
|
+
robot.move_to("pick", offset=[0, 0, 0.05, 0, 0.1, 0]) # full with rotation
|
|
401
403
|
```
|
|
402
404
|
|
|
403
405
|
#### Resolve a Pose
|
|
@@ -416,7 +418,7 @@ robot.move_to(pose) # move to the resolved pose later
|
|
|
416
418
|
from urkit import MoveFrame
|
|
417
419
|
|
|
418
420
|
robot.move_frame = MoveFrame.TOOL # default is BASE
|
|
419
|
-
robot.move_relative(
|
|
421
|
+
robot.move_relative(delta_z=0.05) # 5cm along tool Z
|
|
420
422
|
```
|
|
421
423
|
|
|
422
424
|
- **BASE** (default): delta relative to robot base
|
|
@@ -440,8 +442,9 @@ robot.import_points("backup.json")
|
|
|
440
442
|
#### Relative Moves
|
|
441
443
|
|
|
442
444
|
```python
|
|
443
|
-
robot.move_relative(
|
|
444
|
-
robot.move_relative(
|
|
445
|
+
robot.move_relative(delta_y=0.01) # 1cm along Y
|
|
446
|
+
robot.move_relative(delta_z=0.05, frame=MoveFrame.TOOL) # 5cm along tool Z
|
|
447
|
+
robot.move_relative([0, 0.01, 0, 0, 0, 0])
|
|
445
448
|
```
|
|
446
449
|
|
|
447
450
|
#### Sequences with Blending
|
|
@@ -454,7 +457,17 @@ robot.move_sequence(["a", "b", "c"], blend_radius=0.02)
|
|
|
454
457
|
#### Contact Detection
|
|
455
458
|
|
|
456
459
|
```python
|
|
460
|
+
# Zeros FT sensor automatically, then moves until force exceeds threshold
|
|
457
461
|
robot.move_until_contact([0, 0, -0.02, 0, 0, 0])
|
|
462
|
+
|
|
463
|
+
# Custom threshold (default: 5.0 N/Nm)
|
|
464
|
+
robot.move_until_contact([0, 0, -0.02, 0, 0, 0], threshold=10.0)
|
|
465
|
+
|
|
466
|
+
# Skip zeroing if you need absolute force values
|
|
467
|
+
robot.move_until_contact([0, 0, -0.02, 0, 0, 0], zero_first=False)
|
|
468
|
+
|
|
469
|
+
# Manual zero (e.g. before custom force-based logic)
|
|
470
|
+
robot.zero_ft_sensor()
|
|
458
471
|
```
|
|
459
472
|
|
|
460
473
|
#### Velocity Control
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|