|
11 | 11 | from geometry_msgs.msg import Pose, PoseStamped |
12 | 12 | from scipy.spatial.transform import Rotation |
13 | 13 | from sensor_msgs.msg import JointState |
14 | | -from teleop_ros2_interfaces.msg import HandJointPoses |
| 14 | +from teleop_ros2_interfaces.msg import NamedPoseArray |
15 | 15 |
|
16 | 16 | from isaacteleop.retargeting_engine.interface import OptionalTensorGroup |
17 | 17 | from isaacteleop.retargeting_engine.tensor_types.indices import ( |
|
36 | 36 | ) |
37 | 37 |
|
38 | 38 |
|
| 39 | +def _compose_ee_msg( |
| 40 | + left_pose: Pose | None, |
| 41 | + right_pose: Pose | None, |
| 42 | + now, |
| 43 | + frame_id: str, |
| 44 | +) -> NamedPoseArray: |
| 45 | + msg = NamedPoseArray() |
| 46 | + msg.header.stamp = now |
| 47 | + msg.header.frame_id = frame_id |
| 48 | + for side, pose in (("left", left_pose), ("right", right_pose)): |
| 49 | + # Set the pose and validity together so the parallel fields cannot diverge. |
| 50 | + if pose is None: |
| 51 | + pose = to_pose([0.0, 0.0, 0.0]) |
| 52 | + pose_is_valid = False |
| 53 | + else: |
| 54 | + pose_is_valid = True |
| 55 | + msg.name.append(side) |
| 56 | + msg.pose.append(pose) |
| 57 | + msg.is_valid.append(pose_is_valid) |
| 58 | + return msg |
| 59 | + |
| 60 | + |
| 61 | +def _compute_ee_pose_from_controller( |
| 62 | + ctrl: OptionalTensorGroup, |
| 63 | + side: str, |
| 64 | + transform_rot: Rotation | None = None, |
| 65 | + transform_trans: Sequence[float] | None = None, |
| 66 | + controller_uses_hands_source: bool = False, |
| 67 | +) -> Pose | None: |
| 68 | + if not controller_aim_is_valid(ctrl): |
| 69 | + return None |
| 70 | + |
| 71 | + pos = [float(x) for x in ctrl[ControllerInputIndex.AIM_POSITION]] |
| 72 | + ori = [float(x) for x in ctrl[ControllerInputIndex.AIM_ORIENTATION]] |
| 73 | + pose = to_pose(pos, ori) |
| 74 | + |
| 75 | + if transform_rot is not None or transform_trans is not None: |
| 76 | + pose = apply_transform_to_pose(pose, transform_rot, transform_trans) |
| 77 | + |
| 78 | + if controller_uses_hands_source: |
| 79 | + pose = apply_manus_controller_to_hand_pose(pose, side) |
| 80 | + |
| 81 | + return pose |
| 82 | + |
| 83 | + |
| 84 | +def _compute_ee_pose_from_hand( |
| 85 | + hand: OptionalTensorGroup, |
| 86 | + transform_rot: Rotation | None = None, |
| 87 | + transform_trans: Sequence[float] | None = None, |
| 88 | +) -> Pose | None: |
| 89 | + if not hand_wrist_is_valid(hand): |
| 90 | + return None |
| 91 | + |
| 92 | + positions = np.asarray(hand[HandInputIndex.JOINT_POSITIONS]) |
| 93 | + orientations = np.asarray(hand[HandInputIndex.JOINT_ORIENTATIONS]) |
| 94 | + pose = to_pose( |
| 95 | + positions[HandJointIndex.WRIST], |
| 96 | + orientations[HandJointIndex.WRIST], |
| 97 | + ) |
| 98 | + if transform_rot is not None or transform_trans is not None: |
| 99 | + pose = apply_transform_to_pose(pose, transform_rot, transform_trans) |
| 100 | + return pose |
| 101 | + |
| 102 | + |
39 | 103 | def _to_pose_stamped(pose: Pose, now, frame_id: str) -> PoseStamped: |
40 | 104 | msg = PoseStamped() |
41 | 105 | msg.header.stamp = now |
@@ -119,50 +183,53 @@ def _as_float(ctrl, index): |
119 | 183 | } |
120 | 184 |
|
121 | 185 |
|
122 | | -def build_ee_msg_from_controller( |
123 | | - ctrl: OptionalTensorGroup, |
124 | | - side: str, |
| 186 | +def build_ee_msg_from_controllers( |
| 187 | + left_ctrl: OptionalTensorGroup, |
| 188 | + right_ctrl: OptionalTensorGroup, |
125 | 189 | now, |
126 | 190 | frame_id: str, |
127 | 191 | transform_rot: Rotation | None = None, |
128 | 192 | transform_trans: Sequence[float] | None = None, |
129 | 193 | controller_uses_hands_source: bool = False, |
130 | | -) -> PoseStamped | None: |
131 | | - if not controller_aim_is_valid(ctrl): |
132 | | - return None |
133 | | - |
134 | | - pos = [float(x) for x in ctrl[ControllerInputIndex.AIM_POSITION]] |
135 | | - ori = [float(x) for x in ctrl[ControllerInputIndex.AIM_ORIENTATION]] |
136 | | - pose = to_pose(pos, ori) |
137 | | - |
138 | | - if transform_rot is not None or transform_trans is not None: |
139 | | - pose = apply_transform_to_pose(pose, transform_rot, transform_trans) |
140 | | - |
141 | | - if controller_uses_hands_source: |
142 | | - pose = apply_manus_controller_to_hand_pose(pose, side) |
143 | | - |
144 | | - return _to_pose_stamped(pose, now, frame_id) |
| 194 | +) -> NamedPoseArray: |
| 195 | + """Build fixed left/right EE entries from controller aim poses.""" |
| 196 | + left_pose = _compute_ee_pose_from_controller( |
| 197 | + left_ctrl, |
| 198 | + "left", |
| 199 | + transform_rot, |
| 200 | + transform_trans, |
| 201 | + controller_uses_hands_source, |
| 202 | + ) |
| 203 | + right_pose = _compute_ee_pose_from_controller( |
| 204 | + right_ctrl, |
| 205 | + "right", |
| 206 | + transform_rot, |
| 207 | + transform_trans, |
| 208 | + controller_uses_hands_source, |
| 209 | + ) |
| 210 | + return _compose_ee_msg(left_pose, right_pose, now, frame_id) |
145 | 211 |
|
146 | 212 |
|
147 | | -def build_ee_msg_from_hand( |
148 | | - hand: OptionalTensorGroup, |
| 213 | +def build_ee_msg_from_hands( |
| 214 | + left_hand: OptionalTensorGroup, |
| 215 | + right_hand: OptionalTensorGroup, |
149 | 216 | now, |
150 | 217 | frame_id: str, |
151 | 218 | transform_rot: Rotation | None = None, |
152 | 219 | transform_trans: Sequence[float] | None = None, |
153 | | -) -> PoseStamped | None: |
154 | | - if not hand_wrist_is_valid(hand): |
155 | | - return None |
156 | | - |
157 | | - positions = np.asarray(hand[HandInputIndex.JOINT_POSITIONS]) |
158 | | - orientations = np.asarray(hand[HandInputIndex.JOINT_ORIENTATIONS]) |
159 | | - pose = to_pose( |
160 | | - positions[HandJointIndex.WRIST], |
161 | | - orientations[HandJointIndex.WRIST], |
| 220 | +) -> NamedPoseArray: |
| 221 | + """Build fixed left/right EE entries from hand wrist poses.""" |
| 222 | + left_pose = _compute_ee_pose_from_hand( |
| 223 | + left_hand, |
| 224 | + transform_rot, |
| 225 | + transform_trans, |
162 | 226 | ) |
163 | | - if transform_rot is not None or transform_trans is not None: |
164 | | - pose = apply_transform_to_pose(pose, transform_rot, transform_trans) |
165 | | - return _to_pose_stamped(pose, now, frame_id) |
| 227 | + right_pose = _compute_ee_pose_from_hand( |
| 228 | + right_hand, |
| 229 | + transform_rot, |
| 230 | + transform_trans, |
| 231 | + ) |
| 232 | + return _compose_ee_msg(left_pose, right_pose, now, frame_id) |
166 | 233 |
|
167 | 234 |
|
168 | 235 | def build_finger_joints_msg( |
@@ -214,24 +281,25 @@ def build_full_body_payload(full_body: OptionalTensorGroup) -> Dict: |
214 | 281 | } |
215 | 282 |
|
216 | 283 |
|
217 | | -def build_hand_msg_from_hands( |
| 284 | +def build_hand_msg( |
218 | 285 | left_hand: OptionalTensorGroup, |
219 | 286 | right_hand: OptionalTensorGroup, |
220 | 287 | now, |
221 | 288 | frame_id: str, |
222 | 289 | transform_rot: Rotation | None = None, |
223 | 290 | transform_trans: Sequence[float] | None = None, |
224 | | -) -> HandJointPoses | None: |
225 | | - """Build named hand joint poses for available hands, left then right.""" |
226 | | - if left_hand.is_none and right_hand.is_none: |
227 | | - return None |
228 | | - |
229 | | - msg = HandJointPoses() |
| 291 | +) -> NamedPoseArray: |
| 292 | + """Build fixed left/right hand joint entries with invalid placeholders.""" |
| 293 | + msg = NamedPoseArray() |
230 | 294 | msg.header.stamp = now |
231 | 295 | msg.header.frame_id = frame_id |
232 | 296 |
|
233 | 297 | for side, hand in (("left", left_hand), ("right", right_hand)): |
234 | 298 | if hand.is_none: |
| 299 | + for joint_name in HAND_POSE_NAMES: |
| 300 | + msg.name.append(f"{side}_{joint_name}") |
| 301 | + msg.pose.append(to_pose([0.0, 0.0, 0.0])) |
| 302 | + msg.is_valid.append(False) |
235 | 303 | continue |
236 | 304 | positions = np.asarray(hand[HandInputIndex.JOINT_POSITIONS]) |
237 | 305 | orientations = np.asarray(hand[HandInputIndex.JOINT_ORIENTATIONS]) |
|
0 commit comments