crisp-python 1.4.0__tar.gz → 1.5.0__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 (45) hide show
  1. {crisp_python-1.4.0/crisp_python.egg-info → crisp_python-1.5.0}/PKG-INFO +1 -1
  2. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/robot.py +46 -62
  3. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/robot_config.py +1 -0
  4. crisp_python-1.5.0/crisp_py/utils/geometry.py +107 -0
  5. {crisp_python-1.4.0 → crisp_python-1.5.0/crisp_python.egg-info}/PKG-INFO +1 -1
  6. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_python.egg-info/SOURCES.txt +1 -0
  7. {crisp_python-1.4.0 → crisp_python-1.5.0}/pyproject.toml +1 -1
  8. {crisp_python-1.4.0 → crisp_python-1.5.0}/LICENSE.md +0 -0
  9. {crisp_python-1.4.0 → crisp_python-1.5.0}/MANIFEST.in +0 -0
  10. {crisp_python-1.4.0 → crisp_python-1.5.0}/README.md +0 -0
  11. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/__init__.py +0 -0
  12. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/camera/__init__.py +0 -0
  13. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/camera/camera.py +0 -0
  14. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/camera/camera_config.py +0 -0
  15. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/control/clipped_cartesian_impedance.yaml +0 -0
  16. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/control/default_cartesian_impedance.yaml +0 -0
  17. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/control/default_operational_space_controller.yaml +0 -0
  18. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/control/gravity_compensation.yaml +0 -0
  19. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/control/gravity_compensation_on_plane.yaml +0 -0
  20. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/control/joint_control.yaml +0 -0
  21. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/control/joint_velocity_control.yaml +0 -0
  22. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/control/no_friction_cartesian_impedance.yaml +0 -0
  23. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/gripper_franka.yaml +0 -0
  24. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/gripper_left.yaml +0 -0
  25. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/gripper_left_config.yaml +0 -0
  26. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/gripper_right.yaml +0 -0
  27. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/gripper_right_config.yaml +0 -0
  28. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/gripper_uncalibrated.yaml +0 -0
  29. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/gripper_with_trigger.yaml +0 -0
  30. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/config/trigger_left.yaml +0 -0
  31. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/control/__init__.py +0 -0
  32. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/control/controller_switcher.py +0 -0
  33. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/control/joint_trajectory_controller_client.py +0 -0
  34. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/control/parameters_client.py +0 -0
  35. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/gripper/__init__.py +0 -0
  36. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/gripper/gripper.py +0 -0
  37. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/sensors/sensor.py +0 -0
  38. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/sensors/sensor_config.py +0 -0
  39. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/utils/__init__.py +0 -0
  40. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/utils/callback_monitor.py +0 -0
  41. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_py/utils/diagnostics_viewer.py +0 -0
  42. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_python.egg-info/dependency_links.txt +0 -0
  43. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_python.egg-info/requires.txt +0 -0
  44. {crisp_python-1.4.0 → crisp_python-1.5.0}/crisp_python.egg-info/top_level.txt +0 -0
  45. {crisp_python-1.4.0 → crisp_python-1.5.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crisp-python
3
- Version: 1.4.0
3
+ Version: 1.5.0
4
4
  Summary: Simple python interface to control robots that use crisp_controllers and other ROS2 components.
5
5
  Project-URL: Homepage, https://utiasDSL.github.io/crisp_controllers/
6
6
  Requires-Python: >=3.11
@@ -1,13 +1,12 @@
1
1
  """Provides a client to control the franka robot. It is the easiest way to control the robot using ROS2."""
2
2
 
3
3
  import threading
4
- from dataclasses import dataclass
5
4
  from typing import List
6
5
 
7
6
  import numpy as np
8
7
  import rclpy
9
8
  import rclpy.executors
10
- from geometry_msgs.msg import PoseStamped, WrenchStamped
9
+ from geometry_msgs.msg import PoseStamped, TwistStamped, WrenchStamped
11
10
  from numpy.typing import NDArray
12
11
  from rclpy.callback_groups import ReentrantCallbackGroup
13
12
  from rclpy.node import Node
@@ -20,36 +19,7 @@ from crisp_py.control.joint_trajectory_controller_client import JointTrajectoryC
20
19
  from crisp_py.control.parameters_client import ParametersClient
21
20
  from crisp_py.robot_config import FrankaConfig, RobotConfig
22
21
  from crisp_py.utils.callback_monitor import CallbackMonitor
23
-
24
-
25
- @dataclass
26
- class Pose:
27
- """Compact representation of an SE3 object."""
28
-
29
- position: np.ndarray
30
- orientation: Rotation
31
-
32
- def copy(self) -> "Pose":
33
- """Create a copy of this pose."""
34
- return Pose(self.position.copy(), Rotation.from_quat(self.orientation.as_quat()))
35
-
36
- def __str__(self) -> str:
37
- """Return a string representation of a Pose."""
38
- return f"Pos: {np.array2string(self.position, suppress_small=True, precision=2, floatmode='fixed')},\n Orientation: {np.array2string(self.orientation.as_matrix(), suppress_small=True, precision=2, floatmode='fixed')}"
39
-
40
- def __sub__(self, other: "Pose") -> "Pose":
41
- """Subtract another pose from this pose, i.e. compute the relative pose."""
42
- return Pose(
43
- self.position - other.position,
44
- self.orientation * other.orientation.inv(),
45
- )
46
-
47
- def __add__(self, other: "Pose") -> "Pose":
48
- """Add another pose to this pose, i.e. add a relative pose."""
49
- return Pose(
50
- self.position + other.position,
51
- other.orientation * self.orientation,
52
- )
22
+ from crisp_py.utils.geometry import Pose, Twist
53
23
 
54
24
 
55
25
  class Robot:
@@ -111,6 +81,7 @@ class Robot:
111
81
  self._current_joint = None
112
82
  self._target_joint = None
113
83
  self._target_wrench = None
84
+ self._current_twist = None
114
85
 
115
86
  self._callback_monitor = CallbackMonitor(
116
87
  node=self.node,
@@ -145,6 +116,16 @@ class Robot:
145
116
  callback_group=ReentrantCallbackGroup(),
146
117
  )
147
118
 
119
+ self.node.create_subscription(
120
+ TwistStamped,
121
+ self.config.current_twist_topic,
122
+ self._callback_monitor.monitor(
123
+ f"{namespace.capitalize()} Current Twist", self._callback_current_twist
124
+ ),
125
+ qos_profile_sensor_data,
126
+ callback_group=ReentrantCallbackGroup(),
127
+ )
128
+
148
129
  self.node.create_timer(
149
130
  1.0 / self.config.publish_frequency,
150
131
  self._callback_monitor.monitor(
@@ -237,6 +218,22 @@ class Robot:
237
218
  )
238
219
  return self._target_joint.copy()
239
220
 
221
+ @property
222
+ def end_effector_twist(self) -> Twist:
223
+ """Get the current twist of the end effector.
224
+
225
+ Returns:
226
+ Twist: Current end-effector twist message.
227
+
228
+ Raises:
229
+ RuntimeError: If no twist messages have been received yet.
230
+ """
231
+ if self._current_twist is None:
232
+ raise RuntimeError(
233
+ f"The robot has not received any twists yet. Is the current_twist_topic {self.config.current_twist_topic} correct?"
234
+ )
235
+ return self._current_twist.copy()
236
+
240
237
  def is_ready(self) -> bool:
241
238
  """Check if the robot is ready for operation.
242
239
 
@@ -311,7 +308,11 @@ class Robot:
311
308
  """
312
309
  if self._target_pose is None or not rclpy.ok():
313
310
  return
314
- self._target_pose_publisher.publish(self._pose_to_pose_msg(self._target_pose))
311
+ self._target_pose_publisher.publish(
312
+ self._target_pose.to_ros_msg(
313
+ self.config.base_frame, self.node.get_clock().now().to_msg()
314
+ )
315
+ )
315
316
 
316
317
  def _callback_publish_target_joint(self):
317
318
  """Publish the current target joint configuration if one exists.
@@ -384,10 +385,21 @@ class Robot:
384
385
  Args:
385
386
  msg (PoseStamped): ROS message containing the current pose.
386
387
  """
387
- self._current_pose = self._pose_msg_to_pose(msg)
388
+ self._current_pose = Pose.from_ros_msg(msg)
388
389
  if self._target_pose is None:
389
390
  self._target_pose = self._current_pose.copy()
390
391
 
392
+ def _callback_current_twist(self, msg: TwistStamped):
393
+ """Update the current twist from a ROS message.
394
+
395
+ This callback is triggered when a new twist message is received. It updates
396
+ the current twist.
397
+
398
+ Args:
399
+ msg (TwistStamped): ROS message containing the current twist.
400
+ """
401
+ self._current_twist = Twist.from_ros_msg(msg)
402
+
391
403
  def _callback_current_joint(self, msg: JointState):
392
404
  """Update the current joint state from a ROS message.
393
405
 
@@ -467,34 +479,6 @@ class Robot:
467
479
  # if switch_to_default_controller:
468
480
  # self.controller_switcher_client.switch_controller(self.config.default_controller)
469
481
 
470
- def _pose_msg_to_pose(self, pose: PoseStamped) -> Pose:
471
- """Convert a ROS2 pose msg to a pose."""
472
- position = np.array([pose.pose.position.x, pose.pose.position.y, pose.pose.position.z])
473
- orientation = Rotation.from_quat(
474
- [
475
- pose.pose.orientation.x,
476
- pose.pose.orientation.y,
477
- pose.pose.orientation.z,
478
- pose.pose.orientation.w,
479
- ]
480
- )
481
- return Pose(position, orientation)
482
-
483
- def _pose_to_pose_msg(self, pose: Pose) -> PoseStamped:
484
- """Convert a pose to a pose message."""
485
- msg = PoseStamped()
486
- msg.header.frame_id = self.config.base_frame
487
- msg.header.stamp = self.node.get_clock().now().to_msg()
488
- msg.pose.position.x, msg.pose.position.y, msg.pose.position.z = pose.position
489
- q = pose.orientation.as_quat()
490
- (
491
- msg.pose.orientation.x,
492
- msg.pose.orientation.y,
493
- msg.pose.orientation.z,
494
- msg.pose.orientation.w,
495
- ) = q
496
- return msg
497
-
498
482
  def _joint_to_joint_msg(
499
483
  self, q: NDArray, dq: NDArray | None = None, tau: NDArray | None = None
500
484
  ) -> JointState:
@@ -41,6 +41,7 @@ class RobotConfig:
41
41
  target_joint_topic: str = "target_joint"
42
42
  current_pose_topic: str = "current_pose"
43
43
  current_joint_topic: str = "joint_states"
44
+ current_twist_topic: str = "current_twist"
44
45
 
45
46
  publish_frequency: float = 50.0
46
47
  time_to_home: float = 5.0
@@ -0,0 +1,107 @@
1
+ """Utility functions for geometry operations."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ import numpy as np
6
+ from builtin_interfaces.msg import Time as TimeMsg
7
+ from geometry_msgs.msg import PoseStamped, TwistStamped
8
+ from scipy.spatial.transform import Rotation
9
+
10
+
11
+ @dataclass
12
+ class Pose:
13
+ """Compact representation of an SE3 object."""
14
+
15
+ position: np.ndarray
16
+ orientation: Rotation
17
+
18
+ def copy(self) -> "Pose":
19
+ """Create a copy of this pose."""
20
+ return Pose(self.position.copy(), Rotation.from_quat(self.orientation.as_quat()))
21
+
22
+ def __str__(self) -> str:
23
+ """Return a string representation of a Pose."""
24
+ return f"Pos: {np.array2string(self.position, suppress_small=True, precision=2, floatmode='fixed')},\n Orientation: {np.array2string(self.orientation.as_matrix(), suppress_small=True, precision=2, floatmode='fixed')}"
25
+
26
+ def __sub__(self, other: "Pose") -> "Pose":
27
+ """Subtract another pose from this pose, i.e. compute the relative pose."""
28
+ return Pose(
29
+ self.position - other.position,
30
+ self.orientation * other.orientation.inv(),
31
+ )
32
+
33
+ def __add__(self, other: "Pose") -> "Pose":
34
+ """Add another pose to this pose, i.e. add a relative pose."""
35
+ return Pose(
36
+ self.position + other.position,
37
+ other.orientation * self.orientation,
38
+ )
39
+
40
+ @classmethod
41
+ def from_ros_msg(cls, msg: PoseStamped) -> "Pose":
42
+ """Create Pose from ROS PoseStamped message."""
43
+ position = np.array([msg.pose.position.x, msg.pose.position.y, msg.pose.position.z])
44
+ orientation = Rotation.from_quat(
45
+ [
46
+ msg.pose.orientation.x,
47
+ msg.pose.orientation.y,
48
+ msg.pose.orientation.z,
49
+ msg.pose.orientation.w,
50
+ ]
51
+ )
52
+ return cls(position, orientation)
53
+
54
+ def to_ros_msg(self, frame_id: str, stamp: TimeMsg) -> PoseStamped:
55
+ """Convert Pose to ROS PoseStamped message."""
56
+ msg = PoseStamped()
57
+ msg.header.frame_id = frame_id
58
+ msg.header.stamp = stamp
59
+ msg.pose.position.x, msg.pose.position.y, msg.pose.position.z = self.position
60
+ q = self.orientation.as_quat()
61
+ (
62
+ msg.pose.orientation.x,
63
+ msg.pose.orientation.y,
64
+ msg.pose.orientation.z,
65
+ msg.pose.orientation.w,
66
+ ) = q
67
+ return msg
68
+
69
+
70
+ @dataclass
71
+ class Twist:
72
+ """Compact representation of a twist (linear and angular velocity)."""
73
+
74
+ linear: np.ndarray
75
+ angular: np.ndarray
76
+
77
+ def copy(self) -> "Twist":
78
+ """Create a copy of this twist."""
79
+ return Twist(self.linear.copy(), self.angular.copy())
80
+
81
+ def __str__(self) -> str:
82
+ """Return a string representation of a Twist."""
83
+ return f"Linear: {np.array2string(self.linear, suppress_small=True, precision=2, floatmode='fixed')},\n Angular: {np.array2string(self.angular, suppress_small=True, precision=2, floatmode='fixed')}"
84
+
85
+ def magnitude(self) -> float:
86
+ """Return the magnitude of the linear velocity."""
87
+ return np.linalg.norm(self.linear).astype(float)
88
+
89
+ def angular_magnitude(self) -> float:
90
+ """Return the magnitude of the angular velocity."""
91
+ return np.linalg.norm(self.angular).astype(float)
92
+
93
+ @classmethod
94
+ def from_ros_msg(cls, msg: TwistStamped) -> "Twist":
95
+ """Create Twist from ROS TwistStamped message."""
96
+ linear = np.array([msg.twist.linear.x, msg.twist.linear.y, msg.twist.linear.z])
97
+ angular = np.array([msg.twist.angular.x, msg.twist.angular.y, msg.twist.angular.z])
98
+ return cls(linear, angular)
99
+
100
+ def to_ros_msg(self, frame_id: str, stamp: TimeMsg) -> TwistStamped:
101
+ """Convert Twist to ROS TwistStamped message."""
102
+ msg = TwistStamped()
103
+ msg.header.frame_id = frame_id
104
+ msg.header.stamp = stamp
105
+ msg.twist.linear.x, msg.twist.linear.y, msg.twist.linear.z = self.linear
106
+ msg.twist.angular.x, msg.twist.angular.y, msg.twist.angular.z = self.angular
107
+ return msg
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crisp-python
3
- Version: 1.4.0
3
+ Version: 1.5.0
4
4
  Summary: Simple python interface to control robots that use crisp_controllers and other ROS2 components.
5
5
  Project-URL: Homepage, https://utiasDSL.github.io/crisp_controllers/
6
6
  Requires-Python: >=3.11
@@ -35,6 +35,7 @@ crisp_py/sensors/sensor_config.py
35
35
  crisp_py/utils/__init__.py
36
36
  crisp_py/utils/callback_monitor.py
37
37
  crisp_py/utils/diagnostics_viewer.py
38
+ crisp_py/utils/geometry.py
38
39
  crisp_python.egg-info/PKG-INFO
39
40
  crisp_python.egg-info/SOURCES.txt
40
41
  crisp_python.egg-info/dependency_links.txt
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "crisp-python"
3
- version = "1.4.0"
3
+ version = "1.5.0"
4
4
  description = "Simple python interface to control robots that use crisp_controllers and other ROS2 components."
5
5
  readme = "README.md"
6
6
  license-files = ["LICENSE.md"]
File without changes
File without changes
File without changes
File without changes