Skip to content

Commit 151ccd9

Browse files
authored
Merge pull request #208 from utn-mi/tilburg_grasp
Tilburg Hand Sim & Real integration
2 parents 2c9a024 + c033293 commit 151ccd9

File tree

17 files changed

+895
-52
lines changed

17 files changed

+895
-52
lines changed

examples/simple_tilburg_test.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from time import sleep
2+
3+
import numpy as np
4+
from rcs._core.common import GraspType
5+
from rcs.hand.tilburg_hand import THConfig, TilburgHand
6+
7+
8+
def sim_real_index_flip(q: np.ndarray):
9+
if len(q) == 18:
10+
# This is the qpos for the real robot
11+
q = q[:16]
12+
assert len(q) == 16, "Expected 16 joint positions for the Tilburg hand"
13+
q2 = q.copy()
14+
for i in range(4):
15+
for j in range(4):
16+
q2[i * 4 + j] = q[i * 4 + (3 - j)]
17+
return q2
18+
19+
20+
def sim_real_name_flip(names):
21+
names2 = names.copy()
22+
for i in range(4):
23+
for j in range(4):
24+
names2[i * 4 + j] = names[i * 4 + (3 - j)]
25+
return names2
26+
27+
28+
def pad_qpos(q: np.ndarray):
29+
if len(q) == 16:
30+
# This is the qpos for the real robot
31+
q = np.concatenate((q, np.zeros(2)))
32+
assert len(q) == 18, "Expected 18 joint positions for the Tilburg hand"
33+
return q
34+
35+
36+
if __name__ == "__main__":
37+
config = {
38+
"motor_calib_min_range_deg": [-5, 0, 0, 0, -5, -5, 0, -25, -5, -5, 0, -25, -5, -5, 0, -25, 0, 0],
39+
"motor_calib_max_range_deg": [95, 90, 100, 90, 95, 95, 95, 25, 95, 95, 95, 25, 95, 95, 95, 25, 1, 1],
40+
}
41+
power_grasp_values = [
42+
0.5,
43+
0.5,
44+
0.5,
45+
1.4, # THUMB_(IP, MCP, ABD, CMC)
46+
0.5,
47+
0.5,
48+
1.0,
49+
0.7, # INDEX_(DIP, PIP, MCP, ABD)
50+
0.5,
51+
0.5,
52+
1.0,
53+
0.3,
54+
0.5,
55+
0.5,
56+
1.0,
57+
0.0,
58+
0,
59+
0,
60+
]
61+
rads = []
62+
min_joints_radians = [np.deg2rad(angle) for angle in config["motor_calib_min_range_deg"]]
63+
max_joints_radians = [np.deg2rad(angle) for angle in config["motor_calib_max_range_deg"]]
64+
65+
for i in range(len(config["motor_calib_min_range_deg"])):
66+
joint_angle = (
67+
power_grasp_values[i] * (config["motor_calib_max_range_deg"][i] - config["motor_calib_min_range_deg"][i])
68+
+ config["motor_calib_min_range_deg"][i]
69+
)
70+
rads.append(np.deg2rad(joint_angle))
71+
72+
print(f"Motor {i}: {joint_angle:.2f} degrees")
73+
print("power_grasp_radians=[", ", ".join(f"{rad:.2f}" for rad in rads), "]")
74+
print("min_joints_radians=[", ", ".join(f"{rad:.2f}" for rad in min_joints_radians), "]")
75+
print("max_joints_radians=[", ", ".join(f"{rad:.2f}" for rad in max_joints_radians), "]")
76+
hand_cfg = THConfig(
77+
calibration_file="/home/sbien/Documents/Development/RCS/robot-control-stack/python/rcs/hand/calibration_og.json",
78+
grasp_percentage=1,
79+
hand_orientation="right",
80+
)
81+
hand = TilburgHand(cfg=hand_cfg, verbose=True)
82+
hand.set_grasp_type(GraspType.POWER_GRASP)
83+
hand.grasp()
84+
sleep(2)
85+
hand.open()
86+
sleep(5)
87+
hand.reset()
88+
hand.close()
89+
hand.disconnect()

examples/tilburg_grasp.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import logging
2+
3+
import numpy as np
4+
from rcs._core.common import RobotPlatform
5+
from rcs.envs.base import ControlMode, RelativeTo
6+
from rcs.envs.creators import SimEnvCreator
7+
from rcs.envs.utils import default_sim_robot_cfg, default_sim_tilburg_hand_cfg
8+
9+
logger = logging.getLogger(__name__)
10+
logger.setLevel(logging.INFO)
11+
12+
ROBOT_INSTANCE = RobotPlatform.SIMULATION
13+
14+
15+
def main():
16+
mjcf = "/home/sbien/Documents/Development/RCS/models/scenes/fr3_tilburg_empty_world/scene.xml"
17+
if ROBOT_INSTANCE == RobotPlatform.SIMULATION:
18+
env_rel = SimEnvCreator()(
19+
control_mode=ControlMode.JOINTS,
20+
collision_guard=False,
21+
robot_cfg=default_sim_robot_cfg(),
22+
hand_cfg=default_sim_tilburg_hand_cfg(),
23+
# cameras=default_mujoco_cameraset_cfg(),
24+
mjcf=mjcf,
25+
max_relative_movement=np.deg2rad(5),
26+
relative_to=RelativeTo.LAST_STEP,
27+
)
28+
env_rel.get_wrapper_attr("sim").open_gui()
29+
30+
for _ in range(10):
31+
obs, info = env_rel.reset()
32+
for _ in range(10):
33+
# sample random relative action and execute it
34+
act = env_rel.action_space.sample()
35+
obs, reward, terminated, truncated, info = env_rel.step(act)
36+
if truncated or terminated:
37+
logger.info("Truncated or terminated!")
38+
return
39+
40+
41+
if __name__ == "__main__":
42+
main()

include/rcs/Robot.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ struct GripperState {
103103
virtual ~GripperState(){};
104104
};
105105

106+
enum GraspType { POWER_GRASP = 0,
107+
PRECISION_GRASP,
108+
LATERAL_GRASP,
109+
TRIPOD_GRASP };
110+
struct HandConfig {
111+
virtual ~HandConfig(){};
112+
};
113+
struct HandState {
114+
virtual ~HandState(){};
115+
};
116+
106117
class Robot {
107118
public:
108119
virtual ~Robot(){};
@@ -170,6 +181,36 @@ class Gripper {
170181
virtual void reset() = 0;
171182
};
172183

184+
class Hand {
185+
public:
186+
virtual ~Hand(){};
187+
// TODO: Add low-level control interface for the hand with internal state updates
188+
// Also add an implementation specific set_parameters function that takes
189+
// a deduced config type
190+
// bool set_parameters(const GConfig& cfg);
191+
192+
virtual HandConfig* get_parameters() = 0;
193+
virtual HandState* get_state() = 0;
194+
195+
// set width of the hand, 0 is closed, 1 is open
196+
virtual void set_normalized_joint_poses(const VectorXd& q) = 0;
197+
virtual VectorXd get_normalized_joint_poses() = 0;
198+
199+
virtual bool is_grasped() = 0;
200+
201+
// close hand with force, return true if the object is grasped successfully
202+
virtual void grasp() = 0;
203+
204+
// open hand
205+
virtual void open() = 0;
206+
207+
// close hand without applying force
208+
virtual void shut() = 0;
209+
210+
// puts the hand to max position
211+
virtual void reset() = 0;
212+
};
213+
173214
} // namespace common
174215
} // namespace rcs
175216
#endif // RCS_ROBOT_H

python/rcs/_core/common.pyi

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,21 @@ __all__ = [
1313
"BaseCameraConfig",
1414
"FR3",
1515
"FrankaHandTCPOffset",
16+
"GraspType",
1617
"Gripper",
1718
"GripperConfig",
1819
"GripperState",
1920
"HARDWARE",
21+
"Hand",
22+
"HandConfig",
23+
"HandState",
2024
"IK",
2125
"IdentityRotMatrix",
2226
"IdentityRotQuatVec",
2327
"IdentityTranslation",
28+
"LATERAL_GRASP",
29+
"POWER_GRASP",
30+
"PRECISION_GRASP",
2431
"Pin",
2532
"Pose",
2633
"RL",
@@ -33,6 +40,7 @@ __all__ = [
3340
"RobotType",
3441
"SIMULATION",
3542
"SO101",
43+
"TRIPOD_GRASP",
3644
"UR5e",
3745
"XArm7",
3846
"robots_meta_config",
@@ -47,6 +55,41 @@ class BaseCameraConfig:
4755
resolution_width: int
4856
def __init__(self, identifier: str, frame_rate: int, resolution_width: int, resolution_height: int) -> None: ...
4957

58+
class GraspType:
59+
"""
60+
Members:
61+
62+
POWER_GRASP
63+
64+
PRECISION_GRASP
65+
66+
LATERAL_GRASP
67+
68+
TRIPOD_GRASP
69+
"""
70+
71+
LATERAL_GRASP: typing.ClassVar[GraspType] # value = <GraspType.LATERAL_GRASP: 2>
72+
POWER_GRASP: typing.ClassVar[GraspType] # value = <GraspType.POWER_GRASP: 0>
73+
PRECISION_GRASP: typing.ClassVar[GraspType] # value = <GraspType.PRECISION_GRASP: 1>
74+
TRIPOD_GRASP: typing.ClassVar[GraspType] # value = <GraspType.TRIPOD_GRASP: 3>
75+
__members__: typing.ClassVar[
76+
dict[str, GraspType]
77+
] # value = {'POWER_GRASP': <GraspType.POWER_GRASP: 0>, 'PRECISION_GRASP': <GraspType.PRECISION_GRASP: 1>, 'LATERAL_GRASP': <GraspType.LATERAL_GRASP: 2>, 'TRIPOD_GRASP': <GraspType.TRIPOD_GRASP: 3>}
78+
def __eq__(self, other: typing.Any) -> bool: ...
79+
def __getstate__(self) -> int: ...
80+
def __hash__(self) -> int: ...
81+
def __index__(self) -> int: ...
82+
def __init__(self, value: int) -> None: ...
83+
def __int__(self) -> int: ...
84+
def __ne__(self, other: typing.Any) -> bool: ...
85+
def __repr__(self) -> str: ...
86+
def __setstate__(self, state: int) -> None: ...
87+
def __str__(self) -> str: ...
88+
@property
89+
def name(self) -> str: ...
90+
@property
91+
def value(self) -> int: ...
92+
5093
class Gripper:
5194
def get_normalized_width(self) -> float: ...
5295
def get_parameters(self) -> GripperConfig: ...
@@ -64,6 +107,24 @@ class GripperConfig:
64107
class GripperState:
65108
pass
66109

110+
class Hand:
111+
def __init__(self) -> None: ...
112+
def get_normalized_joint_poses(self) -> numpy.ndarray[tuple[M], numpy.dtype[numpy.float64]]: ...
113+
def get_parameters(self) -> HandConfig: ...
114+
def get_state(self) -> HandState: ...
115+
def grasp(self) -> None: ...
116+
def is_grasped(self) -> bool: ...
117+
def open(self) -> None: ...
118+
def reset(self) -> None: ...
119+
def set_normalized_joint_poses(self, q: numpy.ndarray[tuple[M], numpy.dtype[numpy.float64]]) -> None: ...
120+
def shut(self) -> None: ...
121+
122+
class HandConfig:
123+
def __init__(self) -> None: ...
124+
125+
class HandState:
126+
def __init__(self) -> None: ...
127+
67128
class IK:
68129
def forward(self, q0: numpy.ndarray[tuple[M], numpy.dtype[numpy.float64]], tcp_offset: Pose = ...) -> Pose: ...
69130
def ik(
@@ -256,7 +317,11 @@ def robots_meta_config(robot_type: RobotType) -> RobotMetaConfig: ...
256317

257318
FR3: RobotType # value = <RobotType.FR3: 0>
258319
HARDWARE: RobotPlatform # value = <RobotPlatform.HARDWARE: 1>
320+
LATERAL_GRASP: GraspType # value = <GraspType.LATERAL_GRASP: 2>
321+
POWER_GRASP: GraspType # value = <GraspType.POWER_GRASP: 0>
322+
PRECISION_GRASP: GraspType # value = <GraspType.PRECISION_GRASP: 1>
259323
SIMULATION: RobotPlatform # value = <RobotPlatform.SIMULATION: 0>
260324
SO101: RobotType # value = <RobotType.SO101: 2>
325+
TRIPOD_GRASP: GraspType # value = <GraspType.TRIPOD_GRASP: 3>
261326
UR5e: RobotType # value = <RobotType.UR5e: 1>
262327
XArm7: RobotType # value = <RobotType.XArm7: 3>

python/rcs/_core/sim.pyi

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ __all__ = [
2222
"SimRobot",
2323
"SimRobotConfig",
2424
"SimRobotState",
25+
"SimTilburgHand",
26+
"SimTilburgHandConfig",
27+
"SimTilburgHandState",
2528
"default_free",
2629
"fixed",
2730
"free",
@@ -177,6 +180,36 @@ class SimRobotState(rcs._core.common.RobotState):
177180
@property
178181
def target_angles(self) -> numpy.ndarray[tuple[M], numpy.dtype[numpy.float64]]: ...
179182

183+
class SimTilburgHand(rcs._core.common.Hand):
184+
def __init__(self, sim: Sim, cfg: SimTilburgHandConfig) -> None: ...
185+
def get_parameters(self) -> SimTilburgHandConfig: ...
186+
def get_state(self) -> SimTilburgHandState: ...
187+
def set_parameters(self, cfg: SimTilburgHandConfig) -> bool: ...
188+
189+
class SimTilburgHandConfig(rcs._core.common.HandConfig, typing.Generic[M]):
190+
actuators: list[str]
191+
collision_geoms: list[str]
192+
collision_geoms_fingers: list[str]
193+
grasp_type: rcs._core.common.GraspType
194+
ignored_collision_geoms: list[str]
195+
joints: list[str]
196+
max_joint_position: numpy.ndarray[tuple[M], numpy.dtype[numpy.float64]]
197+
min_joint_position: numpy.ndarray[tuple[M], numpy.dtype[numpy.float64]]
198+
seconds_between_callbacks: float
199+
def __init__(self) -> None: ...
200+
def add_id(self, id: str) -> None: ...
201+
202+
class SimTilburgHandState(rcs._core.common.HandState):
203+
def __init__(self) -> None: ...
204+
@property
205+
def collision(self) -> bool: ...
206+
@property
207+
def is_moving(self) -> bool: ...
208+
@property
209+
def last_commanded_qpos(self) -> numpy.ndarray[tuple[M], numpy.dtype[numpy.float64]]: ...
210+
@property
211+
def last_qpos(self) -> numpy.ndarray[tuple[M], numpy.dtype[numpy.float64]]: ...
212+
180213
default_free: CameraType # value = <CameraType.default_free: 3>
181214
fixed: CameraType # value = <CameraType.fixed: 2>
182215
free: CameraType # value = <CameraType.free: 0>

python/rcs/camera/hw.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def calibrate(
8383
return True
8484

8585
def get_extrinsics(self) -> np.ndarray[tuple[typing.Literal[4], typing.Literal[4]], np.dtype[np.float64]] | None:
86-
return np.eye(4)
86+
return np.eye(4) # type: ignore[return-value]
8787

8888

8989
class HardwareCameraSet(BaseCameraSet):

python/rcs/envs/base.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import gymnasium as gym
99
import numpy as np
10+
from rcs._core.common import Hand
1011
from rcs.camera.interface import BaseCameraSet
1112
from rcs.envs.space_utils import (
1213
ActObsInfoWrapper,
@@ -18,7 +19,6 @@
1819
get_space,
1920
get_space_keys,
2021
)
21-
from rcs.hand.interface import BaseHand
2222

2323
from rcs import common
2424

@@ -749,7 +749,7 @@ class HandWrapper(ActObsInfoWrapper):
749749
BINARY_HAND_CLOSED = 0
750750
BINARY_HAND_OPEN = 1
751751

752-
def __init__(self, env, hand: BaseHand, binary: bool = True):
752+
def __init__(self, env, hand: Hand, binary: bool = True):
753753
super().__init__(env)
754754
self.unwrapped: RobotEnv
755755
self.observation_space: gym.spaces.Dict
@@ -778,7 +778,7 @@ def observation(self, observation: dict[str, Any], info: dict[str, Any]) -> tupl
778778
self._last_hand_cmd if self._last_hand_cmd is not None else self.BINARY_HAND_OPEN
779779
)
780780
else:
781-
observation[self.hand_key] = self._hand.get_normalized_joints_poses()
781+
observation[self.hand_key] = self._hand.get_normalized_joint_poses()
782782

783783
info = {}
784784
return observation, info
@@ -798,7 +798,7 @@ def action(self, action: dict[str, Any]) -> dict[str, Any]:
798798
else:
799799
self._hand.open()
800800
else:
801-
self._hand.set_normalized_joints_poses(hand_action)
801+
self._hand.set_normalized_joint_poses(hand_action)
802802
self._last_hand_cmd = hand_action
803803
del action[self.hand_key]
804804
return action

0 commit comments

Comments
 (0)