Skip to content

Commit a652087

Browse files
committed
format: cpp and python
1 parent c85489a commit a652087

9 files changed

Lines changed: 120 additions & 90 deletions

File tree

examples/teleop/franka.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
from time import sleep
44

55
import numpy as np
6-
import rcs
7-
from rcs._core.common import RPY, Pose, RobotPlatform
86
from rcs._core import common
7+
from rcs._core.common import RPY, Pose, RobotPlatform
98
from rcs._core.sim import SimConfig
109
from rcs.camera.hw import HardwareCameraSet
1110
from rcs.envs.base import (
@@ -18,15 +17,17 @@
1817
from rcs.envs.creators import SimMultiEnvCreator
1918
from rcs.envs.storage_wrapper import StorageWrapper
2019
from rcs.envs.utils import default_digit, default_sim_gripper_cfg, default_sim_robot_cfg
20+
from rcs.operator.gello import GelloArmConfig, GelloConfig, GelloOperator
21+
from rcs.operator.interface import TeleopLoop
2122
from rcs.operator.quest import QuestConfig, QuestOperator
2223
from rcs.utils import SimpleFrameRate
2324
from rcs_fr3.creators import RCSFR3MultiEnvCreator
2425
from rcs_fr3.utils import default_fr3_hw_gripper_cfg, default_fr3_hw_robot_cfg
2526
from rcs_realsense.utils import default_realsense
26-
from rcs.operator.gello import GelloConfig, GelloOperator, GelloArmConfig
27-
from rcs.operator.interface import TeleopLoop
2827
from simpub.sim.mj_publisher import MujocoPublisher
2928

29+
import rcs
30+
3031
logger = logging.getLogger(__name__)
3132

3233

@@ -64,8 +65,10 @@
6465
DATASET_PATH = "test_data_iris_dual_arm14"
6566
INSTRUCTION = "build a tower with the blocks in front of you"
6667

67-
robot2world={"right": rcs.common.Pose(translation=[0, 0, 0], rpy_vector=[0.89360858, -0.17453293, 0.46425758]),
68-
"left": rcs.common.Pose(translation=[0, 0, 0], rpy_vector=[-0.89360858, -0.17453293, -0.46425758])}
68+
robot2world = {
69+
"right": rcs.common.Pose(translation=[0, 0, 0], rpy_vector=[0.89360858, -0.17453293, 0.46425758]),
70+
"left": rcs.common.Pose(translation=[0, 0, 0], rpy_vector=[-0.89360858, -0.17453293, -0.46425758]),
71+
}
6972

7073
config = QuestConfig(mq3_addr=MQ3_ADDR, simulation=ROBOT_INSTANCE == RobotPlatform.SIMULATION)
7174
# config = GelloConfig(
@@ -94,7 +97,9 @@ def get_env():
9497
robot_cfg=default_fr3_hw_robot_cfg(async_control=True),
9598
control_mode=config.operator_class.control_mode[0],
9699
gripper_cfg=default_fr3_hw_gripper_cfg(async_control=True),
97-
max_relative_movement = 0.5 if config.operator_class.control_mode[0] == ControlMode.JOINTS else (0.5, np.deg2rad(90)),
100+
max_relative_movement=(
101+
0.5 if config.operator_class.control_mode[0] == ControlMode.JOINTS else (0.5, np.deg2rad(90))
102+
),
98103
relative_to=config.operator_class.control_mode[1],
99104
robot2world=robot2world,
100105
)

python/rcs/envs/base.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ class ArmObsType(TQuatDictType, JointsDictType, TRPYDictType): ...
167167
CartOrJointContType: TypeAlias = TQuatDictType | JointsDictType | TRPYDictType
168168
LimitedCartOrJointContType: TypeAlias = LimitedTQuatRelDictType | LimitedJointsRelDictType | LimitedTRPYRelDictType
169169

170+
170171
class ArmWithGripper(TQuatDictType, GripperDictType): ...
171172

172173

@@ -312,7 +313,9 @@ def close(self):
312313
class MultiRobotWrapper(gym.Env):
313314
"""Wraps a dictionary of environments to allow for multi robot control."""
314315

315-
def __init__(self, envs: dict[str, gym.Env] | dict[str, gym.Wrapper], robot2world: dict[str, common.Pose] | None = None):
316+
def __init__(
317+
self, envs: dict[str, gym.Env] | dict[str, gym.Wrapper], robot2world: dict[str, common.Pose] | None = None
318+
):
316319
self.envs = envs
317320
self.unwrapped_multi = cast(dict[str, RobotEnv], {key: env.unwrapped for key, env in envs.items()})
318321
if robot2world is None:

python/rcs/envs/sim.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,16 @@ def step(self, action: dict[str, Any]) -> tuple[dict[str, Any], float, bool, boo
6060
self.sim.step_until_convergence()
6161
# state = self.sim_robot.get_state()
6262
# if "collision" not in info:
63-
# info["collision"] = state.collision
63+
# info["collision"] = state.collision
6464
# else:
65-
# info["collision"] = info["collision"] or state.collision
65+
# info["collision"] = info["collision"] or state.collision
6666
# info["ik_success"] = state.ik_success
6767
info["is_sim_converged"] = self.sim.is_converged()
6868
# truncate episode if collision
6969
# obs.update(self.unwrapped.get_obs())
7070
# return obs, 0, False, info["collision"] or not state.ik_success, info
7171
return obs, 0, False, False, info
7272

73-
7473
def reset(
7574
self, *, seed: int | None = None, options: dict[str, Any] | None = None
7675
) -> tuple[dict[str, Any], dict[str, Any]]:

python/rcs/envs/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ def default_tilburg_hw_hand_cfg(file: str | PathLike | None = None) -> THConfig:
3939

4040
def default_sim_gripper_cfg(idx: str = "0") -> sim.SimGripperConfig:
4141
cfg = sim.SimGripperConfig()
42-
cfg.collision_geoms = []
43-
cfg.collision_geoms_fingers = []
42+
cfg.collision_geoms = []
43+
cfg.collision_geoms_fingers = []
4444
# cfg.add_id(idx)
4545
return cfg
4646

python/rcs/operator/gello.py

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import threading
44
import time
55
from dataclasses import dataclass, field
6-
from typing import Any, Dict, List, Optional, Sequence, Tuple, TypedDict, Iterator
6+
from typing import Any, Dict, Iterator, List, Optional, Sequence, Tuple, TypedDict
77

88
import numpy as np
99

@@ -13,17 +13,25 @@
1313
from dynamixel_sdk.packet_handler import PacketHandler
1414
from dynamixel_sdk.port_handler import PortHandler
1515
from dynamixel_sdk.robotis_def import COMM_SUCCESS
16+
1617
HAS_DYNAMIXEL_SDK = True
1718
except ImportError:
1819
HAS_DYNAMIXEL_SDK = False
1920

2021
try:
2122
from pynput import keyboard
23+
2224
HAS_PYNPUT = True
2325
except ImportError:
2426
HAS_PYNPUT = False
2527

26-
from rcs.envs.base import ArmWithGripper, ControlMode, RelativeTo, JointsDictType, GripperDictType
28+
from rcs.envs.base import (
29+
ArmWithGripper,
30+
ControlMode,
31+
GripperDictType,
32+
JointsDictType,
33+
RelativeTo,
34+
)
2735
from rcs.operator.interface import BaseOperator, BaseOperatorConfig, TeleopCommands
2836
from rcs.sim.sim import Sim
2937
from rcs.utils import SimpleFrameRate
@@ -95,7 +103,7 @@ def __init__(
95103
def write_value_by_name(self, name: str, values: Sequence[int | None]):
96104
if len(values) != len(self._ids):
97105
raise ValueError(f"The length of {name} must match the number of servos")
98-
106+
99107
handler = self._groupSyncWriteHandlers[name]
100108
value_len = XL330_CONTROL_TABLE[name]["len"]
101109

@@ -105,7 +113,7 @@ def write_value_by_name(self, name: str, values: Sequence[int | None]):
105113
continue
106114
param = [(value >> (8 * i)) & 0xFF for i in range(value_len)]
107115
handler.addParam(dxl_id, param)
108-
116+
109117
comm_result = handler.txPacket()
110118
if comm_result != COMM_SUCCESS:
111119
handler.clearParam()
@@ -121,7 +129,7 @@ def read_value_by_name(self, name: str) -> List[int]:
121129
comm_result = handler.txRxPacket()
122130
if comm_result != COMM_SUCCESS:
123131
raise RuntimeError(f"Failed to sync read {name}: {self._packetHandler.getTxRxResult(comm_result)}")
124-
132+
125133
values = []
126134
for dxl_id in self._ids:
127135
if handler.isAvailable(dxl_id, addr, value_len):
@@ -177,34 +185,24 @@ def close(self):
177185

178186
# --- Gello Hardware Interface Logic ---
179187

188+
180189
@dataclass
181190
class GelloArmConfig:
182191
com_port: str = "/dev/ttyUSB0"
183192
num_arm_joints: int = 7
184193
joint_signs: List[int] = field(default_factory=lambda: [1, -1, 1, -1, 1, 1, 1])
185194
gripper: bool = True
186195
gripper_range_rad: List[float] = field(default_factory=lambda: [2.23, 3.22])
187-
assembly_offsets: List[float] = field(
188-
default_factory=lambda: [0.000, 0.000, 3.142, 3.142, 3.142, 4.712, 0.000]
189-
)
190-
dynamixel_kp_p: List[int] = field(
191-
default_factory=lambda: [30, 60, 0, 30, 0, 0, 0, 50]
192-
)
193-
dynamixel_kp_i: List[int] = field(
194-
default_factory=lambda: [0, 0, 0, 0, 0, 0, 0, 0]
195-
)
196-
dynamixel_kp_d: List[int] = field(
197-
default_factory=lambda: [250, 100, 80, 60, 30, 10, 5, 0]
198-
)
199-
dynamixel_torque_enable: List[int] = field(
200-
default_factory=lambda: [0, 0, 0, 0, 0, 0, 0, 0]
201-
)
196+
assembly_offsets: List[float] = field(default_factory=lambda: [0.000, 0.000, 3.142, 3.142, 3.142, 4.712, 0.000])
197+
dynamixel_kp_p: List[int] = field(default_factory=lambda: [30, 60, 0, 30, 0, 0, 0, 50])
198+
dynamixel_kp_i: List[int] = field(default_factory=lambda: [0, 0, 0, 0, 0, 0, 0, 0])
199+
dynamixel_kp_d: List[int] = field(default_factory=lambda: [250, 100, 80, 60, 30, 10, 5, 0])
200+
dynamixel_torque_enable: List[int] = field(default_factory=lambda: [0, 0, 0, 0, 0, 0, 0, 0])
202201
dynamixel_goal_position: List[float] = field(
203202
default_factory=lambda: [0.0, 0.0, 0.0, -1.571, 0.0, 1.571, 0.0, 3.509]
204203
)
205204

206205

207-
208206
@dataclass
209207
class DynamixelControlConfig:
210208
kp_p: List[int] = field(default_factory=list)
@@ -289,8 +287,11 @@ def __init__(self, config: GelloArmConfig):
289287

290288
@staticmethod
291289
def normalize_joint_positions(raw, offsets, signs):
292-
return (np.mod((raw - offsets) * signs - GelloHardware.MID_JOINT_POSITIONS, 2 * np.pi)
293-
- np.pi + GelloHardware.MID_JOINT_POSITIONS)
290+
return (
291+
np.mod((raw - offsets) * signs - GelloHardware.MID_JOINT_POSITIONS, 2 * np.pi)
292+
- np.pi
293+
+ GelloHardware.MID_JOINT_POSITIONS
294+
)
294295

295296
def _initialize_parameters(self):
296297
for name, value in self._dynamixel_control_config:
@@ -300,25 +301,29 @@ def _initialize_parameters(self):
300301
def get_joint_and_gripper_positions(self) -> Tuple[np.ndarray, float]:
301302
joints_raw = self._driver.get_joints()
302303
arm_joints_raw = joints_raw[: self._num_arm_joints]
303-
304+
304305
arm_joints_delta = (arm_joints_raw - self._prev_arm_joints_raw) * self._joint_signs
305306
arm_joints = self._prev_arm_joints + arm_joints_delta
306307
self._prev_arm_joints = arm_joints.copy()
307308
self._prev_arm_joints_raw = arm_joints_raw.copy()
308309

309310
arm_joints_clipped = np.clip(arm_joints, self.JOINT_POSITION_LIMITS[:, 0], self.JOINT_POSITION_LIMITS[:, 1])
310-
311+
311312
gripper_pos = 0.0
312313
if self._gripper:
313314
raw_grp = joints_raw[-1]
314-
gripper_pos = (raw_grp - self._gripper_range_rad[0]) / (self._gripper_range_rad[1] - self._gripper_range_rad[0])
315+
gripper_pos = (raw_grp - self._gripper_range_rad[0]) / (
316+
self._gripper_range_rad[1] - self._gripper_range_rad[0]
317+
)
315318
gripper_pos = max(0.0, min(1.0, gripper_pos))
316319

317320
return arm_joints_clipped, gripper_pos
318321

319322
def _goal_position_to_pulses(self, goals):
320323
arm_goals = np.array(goals[: self._num_arm_joints])
321-
initial_rotations = np.floor_divide(self._initial_arm_joints_raw - self._assembly_offsets - self.MID_JOINT_POSITIONS, 2 * np.pi)
324+
initial_rotations = np.floor_divide(
325+
self._initial_arm_joints_raw - self._assembly_offsets - self.MID_JOINT_POSITIONS, 2 * np.pi
326+
)
322327
arm_goals_raw = (initial_rotations * 2 * np.pi + arm_goals + self._assembly_offsets) * self._joint_signs + np.pi
323328
goals_raw = np.append(arm_goals_raw, goals[-1]) if self._gripper else arm_goals_raw
324329
return [self._driver._rad_to_pulses(rad) for rad in goals_raw]
@@ -334,8 +339,6 @@ def close(self):
334339
# --- RCS Operator Implementation ---
335340

336341

337-
338-
339342
class GelloOperator(BaseOperator):
340343
control_mode = (ControlMode.JOINTS, RelativeTo.NONE)
341344

@@ -353,7 +356,7 @@ def __init__(self, config: GelloConfig, sim: Sim | None = None):
353356
self._last_joints = {name: None for name in self.controller_names}
354357
self._last_gripper = {name: 1.0 for name in self.controller_names}
355358
self._hws: Dict[str, GelloHardware] = {}
356-
359+
357360
if HAS_PYNPUT:
358361
self._listener = keyboard.Listener(on_press=self._on_press)
359362
self._listener.start()
@@ -428,8 +431,9 @@ def close(self):
428431
if self.is_alive() and threading.current_thread() != self:
429432
self.join(timeout=1.0)
430433

434+
431435
@dataclass(kw_only=True)
432436
class GelloConfig(BaseOperatorConfig):
433437
operator_class = GelloOperator
434438
# Dictionary for multi-arm setups: {"left": GelloArmConfig(...), "right": GelloArmConfig(...)}
435-
arms: Dict[str, GelloArmConfig] = field(default_factory=lambda: {"right": GelloArmConfig()})
439+
arms: Dict[str, GelloArmConfig] = field(default_factory=lambda: {"right": GelloArmConfig()})

python/rcs/operator/interface.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
from abc import ABC
21
import copy
3-
from dataclasses import dataclass, field
42
import logging
53
import threading
64
import time
5+
from abc import ABC
6+
from dataclasses import dataclass, field
77
from time import sleep
8-
import gymnasium as gym
98

9+
import gymnasium as gym
1010
from rcs.envs.base import ArmWithGripper, ControlMode, RelativeTo
1111
from rcs.sim.sim import Sim
1212
from rcs.utils import SimpleFrameRate
@@ -52,12 +52,14 @@ def consume_action(self) -> dict[str, ArmWithGripper]:
5252
def close(self):
5353
pass
5454

55+
5556
@dataclass(kw_only=True)
5657
class BaseOperatorConfig:
5758
operator_class: BaseOperator
5859
read_frequency: int = 30
5960
simulation: bool = True
6061

62+
6163
class TeleopLoop:
6264
"""Interface for an operator device"""
6365

@@ -83,7 +85,7 @@ def __init__(
8385
self.key_translation = key_translation
8486

8587
# Absolute operators (RelativeTo.NONE) need an initial sync
86-
self._synced = (self.operator.control_mode[1] != RelativeTo.NONE)
88+
self._synced = self.operator.control_mode[1] != RelativeTo.NONE
8789

8890
def stop(self):
8991
self.operator.close()
@@ -108,13 +110,13 @@ def _translate_keys(self, actions):
108110
if robot_name in self._last_obs:
109111
translated[robot_name] = {
110112
"joints": self._last_obs[robot_name]["joints"].copy(),
111-
"gripper": self._last_obs[robot_name].get("gripper", 1.0)
113+
"gripper": self._last_obs[robot_name].get("gripper", 1.0),
112114
}
113115
return translated
114116

115117
def environment_step_loop(self):
116118
rate_limiter = SimpleFrameRate(self.env_frequency, "env loop")
117-
119+
118120
# 0. Initial Reset to get current positions for untracked robots
119121
self._last_obs, _ = self.env.reset()
120122

@@ -135,15 +137,15 @@ def environment_step_loop(self):
135137
sleep(1) # sleep to let the robot reach the goal
136138
self._last_obs, _ = self.env.reset()
137139
self.operator.reset_operator_state()
138-
self._synced = (self.operator.control_mode[1] != RelativeTo.NONE)
140+
self._synced = self.operator.control_mode[1] != RelativeTo.NONE
139141
# consume new commands because of potential origin reset
140142
continue
141143

142144
elif cmds.failure:
143145
print("Command: Failure! Resetting env...")
144146
self._last_obs, _ = self.env.reset()
145147
self.operator.reset_operator_state()
146-
self._synced = (self.operator.control_mode[1] != RelativeTo.NONE)
148+
self._synced = self.operator.control_mode[1] != RelativeTo.NONE
147149
# consume new commands because of potential origin reset
148150
continue
149151

@@ -174,7 +176,8 @@ def environment_step_loop(self):
174176
robot = self.key_translation[controller]
175177
print(f"Command: Resetting origin for {robot}...")
176178
assert (
177-
self.operator.control_mode[1] == RelativeTo.CONFIGURED_ORIGIN
179+
self.operator.control_mode[1]
180+
== RelativeTo.CONFIGURED_ORIGIN
178181
# TODO the following is a dict and can thus not easily be used like this
179182
# and self.env.get_wrapper_attr("relative_to") == RelativeTo.CONFIGURED_ORIGIN
180183
), "both robot env and operator must be configured to relative_to.CONFIGURED_ORIGIN"

0 commit comments

Comments
 (0)