forked from dexsuite/dex-retargeting
-
Notifications
You must be signed in to change notification settings - Fork 0
/
kinematics_adaptor.py
102 lines (80 loc) · 3.85 KB
/
kinematics_adaptor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
from abc import abstractmethod
from typing import List
import numpy as np
from dex_retargeting.robot_wrapper import RobotWrapper
class KinematicAdaptor:
def __init__(self, robot: RobotWrapper, target_joint_names: List[str]):
self.robot = robot
self.target_joint_names = target_joint_names
# Index mapping
self.idx_pin2target = np.array([robot.get_joint_index(n) for n in target_joint_names])
@abstractmethod
def forward_qpos(self, qpos: np.ndarray) -> np.ndarray:
"""
Adapt the joint position for different kinematics constraints.
Note that the joint order of this qpos is consistent with pinocchio
Args:
qpos: the pinocchio qpos
Returns: the adapted qpos with the same shape as input
"""
pass
@abstractmethod
def backward_jacobian(self, jacobian: np.ndarray) -> np.ndarray:
"""
Adapt the jacobian for different kinematics applications.
Note that the joint order of this Jacobian is consistent with pinocchio
Args:
jacobian: the original jacobian
Returns: the adapted jacobian with the same shape as input
"""
pass
class MimicJointKinematicAdaptor(KinematicAdaptor):
def __init__(
self,
robot: RobotWrapper,
target_joint_names: List[str],
source_joint_names: List[str],
mimic_joint_names: List[str],
multipliers: List[float],
offsets: List[float],
):
super().__init__(robot, target_joint_names)
self.multipliers = np.array(multipliers)
self.offsets = np.array(offsets)
# Joint name check
union_set = set(mimic_joint_names).intersection(set(target_joint_names))
if len(union_set) > 0:
raise ValueError(
f"Mimic joint should not be one of the target joints.\n"
f"Mimic joints: {mimic_joint_names}.\n"
f"Target joints: {target_joint_names}\n"
f"You need to specify the target joint names explicitly in your retargeting config"
f" for robot with mimic joint constraints: {target_joint_names}"
)
# Indices in the pinocchio
self.idx_pin2source = np.array([robot.get_joint_index(name) for name in source_joint_names])
self.idx_pin2mimic = np.array([robot.get_joint_index(name) for name in mimic_joint_names])
# Indices in the output results
self.idx_target2source = np.array([self.target_joint_names.index(n) for n in source_joint_names])
# Dimension check
len_source, len_mimic = self.idx_target2source.shape[0], self.idx_pin2mimic.shape[0]
len_mul, len_offset = self.multipliers.shape[0], self.offsets.shape[0]
if not (len_mimic == len_source == len_mul == len_offset):
raise ValueError(
f"Mimic joints setting dimension mismatch.\n"
f"Source joints: {len_source}, mimic joints: {len_mimic}, multiplier: {len_mul}, offset: {len_offset}"
)
self.num_active_joints = len(robot.dof_joint_names) - len_mimic
# Uniqueness check
if len(mimic_joint_names) != len(np.unique(mimic_joint_names)):
raise ValueError(f"Redundant mimic joint names: {mimic_joint_names}")
def forward_qpos(self, pin_qpos: np.ndarray) -> np.ndarray:
mimic_qpos = pin_qpos[self.idx_pin2source] * self.multipliers + self.offsets
pin_qpos[self.idx_pin2mimic] = mimic_qpos
return pin_qpos
def backward_jacobian(self, jacobian: np.ndarray) -> np.ndarray:
target_jacobian = jacobian[..., self.idx_pin2target]
mimic_joint_jacobian = jacobian[..., self.idx_pin2mimic] * self.multipliers
for i, index in enumerate(self.idx_target2source):
target_jacobian[..., index] += mimic_joint_jacobian[..., i]
return target_jacobian