Skip to content

Commit 4864c53

Browse files
authored
Merge pull request #264 from RobotControlStack/juelg/teleop-interface
feat: operator interface
2 parents 95a5380 + ac4d6a8 commit 4864c53

19 files changed

Lines changed: 1291 additions & 397 deletions

File tree

assets/scenes/fr3_simple_pick_up/scene.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
<camera name="side_view" pos="1.760 -1.205 0.621" xyaxes="0.681 0.733 0.000 -0.105 0.097 0.990"/>
2929

3030
<body name="box_geom" pos="0.44 0.1 0.03" quat="0 0 0 1">
31-
<geom type="box" size="0.032 0.016 0.0288" rgba="0 0.984 0.373 1" name="box_geom" friction="1 0.3 0.1" density="50" />
31+
<geom type="box" size="0.032 0.016 0.0288" rgba="0 0.984 0.373 1" name="box_geom" friction="1 0.3 0.1" density="50" group="1" />
3232
<joint type="free" name="box_joint" />
3333
</body>
3434
</worldbody>

examples/teleop/README.md

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,35 @@
1-
# Teleoperation with Meta Quest
1+
# Franka Teleoperation
2+
3+
## Teleoperation with Meta Quest 3
24
Teleoperate your robot (optinally dual arm) with the Meta Quest
35

4-
## How does it work?
5-
In the script [`quest_iris_dual_arm.py`](quest_iris_dual_arm.py) we use the [IRIS platform](https://intuitive-robots.github.io/iris-project-page/index.html) to get controller poses from the meta quest.
6+
### How does it work?
7+
In the script [`franka.py`](franka.py) we use the [IRIS platform](https://intuitive-robots.github.io/iris-project-page/index.html) to get controller poses from the meta quest.
68
With the relative space wrapper and the relative to configured origin setting theses poses are then apply to the robot in a delta fashion whenever the trigger button is pressed.
79
The buttons are used to start and stop data recording with the [`StorageWrapper`](robot-control-stack/python/rcs/envs/storage_wrapper.py).
810

9-
## Installation
11+
### Installation
1012
[Install RCS](https://robotcontrolstack.org/getting_started/index.html) and the [FR3 extension](https://robotcontrolstack.org/extensions/rcs_fr3.html) (the script is writte for the FR3 as example but can be easily adapted for other robots).
11-
Install the IRIS APK on your quest following [these instructions](https://github.com/intuitive-robots/IRIS-Meta-Quest3).
12-
Finally, install [SimPub](https://github.com/intuitive-robots/SimPublisher) the IRIS python client by
13+
Install the IRIS APK on your quest following [these instructions](https://github.com/intuitive-robots/IRIS-Meta-Quest3) use the apk released [here](https://github.com/RobotControlStack/IRIS-Meta-Quest3/releases/tag/rcsv1) to ensure compatiblity.
14+
Finally, install [SimPub](https://github.com/intuitive-robots/SimPublisher) the IRIS python client by (make sure to install it via requirements file to have the correct version)
1315

1416
```shell
1517
pip install -r requirements.txt
1618
```
1719

18-
## Configuration
20+
### Configuration
1921

20-
### Teleoperating in sim
22+
#### Teleoperating in sim
2123

22-
1. go to [`quest_iris_dual_arm.py`](quest_iris_dual_arm.py) and set `ROBOT_INSTANCE = RobotPlatform.SIMULATION`
24+
1. go to [`franka.py`](quest_iris_dual_arm.py) and set `ROBOT_INSTANCE = RobotPlatform.SIMULATION`
2325

24-
### Teleoperating a real robot
26+
#### Teleoperating a real robot
2527
Note that dual arm is only supported for a aloha like setup where the robot face each other (for more advanced setups you need to change the transformation between the robots yourself).
2628
1. put your robots into FCI mode
27-
2. go to [`quest_iris_dual_arm.py`](quest_iris_dual_arm.py), set `ROBOT_INSTANCE = RobotPlatform.HARDWARE` and set your IP addresses of your robots. Remove the left robot if you only have one.
29+
2. go to [`franka.py`](quest_iris_dual_arm.py), set `ROBOT_INSTANCE = RobotPlatform.HARDWARE` and set your IP addresses of your robots. Remove the left robot if you only have one.
2830

2931

30-
## Running
32+
### Running
3133
1. make sure your computer and quest is in the same subnetwork and they can ping each other.
3234
2. start IRIS meta quest app on your quest (it should be located in the Library under "Unkown Sources" after installation)
3335
3. run the [`quest_align_frame.py`](quest_align_frame.py) script once. Navigate to the link printed on the top likly [http://127.0.0.1:7000](http://127.0.0.1:7000).
@@ -39,5 +41,13 @@ Note that dual arm is only supported for a aloha like setup where the robot face
3941
5. use the right controller to change the orientation of the coordinate axis to fit your right robot (for franka: x front, y left, z up)
4042
6. click the "teleportation scene" button on the still open website
4143
7. cancel the script
42-
8. start the teleoperation script [`quest_iris_dual_arm.py`](quest_iris_dual_arm.py) and enjoy.
44+
8. start the teleoperation script [`franka.py`](quest_iris_dual_arm.py) and enjoy.
45+
4346

47+
## Teleoperation with Franka GELLO Duo
48+
Teleoperate your Franka Duo using the [Franka GELLO Duo](https://franka.de/de-de/product-prototypes).
49+
Install dependencies via
50+
```shell
51+
pip install -r requirements.txt
52+
```
53+
and make sure the `GelloConfig` is commented in and the `QuestConfig` is commented out and adapt your USB IDs to it.

examples/teleop/franka.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import logging
2+
from typing import Any
3+
4+
import numpy as np
5+
from rcs._core import common
6+
from rcs._core.common import RobotPlatform
7+
from rcs._core.sim import SimConfig
8+
from rcs.camera.hw import HardwareCameraSet
9+
from rcs.envs.base import ControlMode
10+
from rcs.envs.creators import SimMultiEnvCreator
11+
from rcs.envs.utils import default_digit, default_sim_gripper_cfg, default_sim_robot_cfg
12+
from rcs.operator.gello import GelloConfig, GelloOperator
13+
from rcs.operator.interface import TeleopLoop
14+
from rcs.operator.quest import QuestConfig, QuestOperator
15+
from rcs_fr3.creators import RCSFR3MultiEnvCreator
16+
from rcs_fr3.utils import default_fr3_hw_gripper_cfg, default_fr3_hw_robot_cfg
17+
from rcs_realsense.utils import default_realsense
18+
from simpub.sim.mj_publisher import MujocoPublisher
19+
20+
import rcs
21+
22+
logger = logging.getLogger(__name__)
23+
24+
25+
ROBOT2IP = {
26+
"right": "192.168.102.1",
27+
"left": "192.168.101.1",
28+
}
29+
ROBOT2ID = {
30+
"left": "1",
31+
"right": "0",
32+
}
33+
34+
35+
# ROBOT_INSTANCE = RobotPlatform.SIMULATION
36+
ROBOT_INSTANCE = RobotPlatform.HARDWARE
37+
38+
RECORD_FPS = 30
39+
# set camera dict to none disable cameras
40+
# CAMERA_DICT = {
41+
# "left_wrist": "230422272017",
42+
# "right_wrist": "230422271040",
43+
# "side": "243522070385",
44+
# "bird_eye": "243522070364",
45+
# }
46+
CAMERA_DICT = None
47+
MQ3_ADDR = "10.42.0.1"
48+
49+
# DIGIT_DICT = {
50+
# "digit_right_left": "D21182",
51+
# "digit_right_right": "D21193"
52+
# }
53+
DIGIT_DICT = None
54+
55+
56+
DATASET_PATH = "test_data_iris_dual_arm14"
57+
INSTRUCTION = "build a tower with the blocks in front of you"
58+
59+
robot2world = {
60+
"right": rcs.common.Pose(
61+
translation=np.array([0, 0, 0]), rpy_vector=np.array([0.89360858, -0.17453293, 0.46425758])
62+
),
63+
"left": rcs.common.Pose(
64+
translation=np.array([0, 0, 0]), rpy_vector=np.array([-0.89360858, -0.17453293, -0.46425758])
65+
),
66+
}
67+
68+
config: QuestConfig | GelloConfig
69+
config = QuestConfig(mq3_addr=MQ3_ADDR, simulation=ROBOT_INSTANCE == RobotPlatform.SIMULATION)
70+
# config = GelloConfig(
71+
# arms={
72+
# "right": GelloArmConfig(com_port="/dev/serial/by-id/usb-ROBOTIS_OpenRB-150_E505008B503059384C2E3120FF07332D-if00"),
73+
# "left": GelloArmConfig(com_port="/dev/serial/by-id/usb-ROBOTIS_OpenRB-150_ABA78B05503059384C2E3120FF062F26-if00"),
74+
# },
75+
# simulation=ROBOT_INSTANCE == RobotPlatform.SIMULATION,
76+
# )
77+
78+
79+
def get_env():
80+
if ROBOT_INSTANCE == RobotPlatform.HARDWARE:
81+
82+
cams: list[Any] = []
83+
if CAMERA_DICT is not None:
84+
cams.append(default_realsense(CAMERA_DICT))
85+
if DIGIT_DICT is not None:
86+
cams.append(default_digit(DIGIT_DICT))
87+
88+
camera_set = HardwareCameraSet(cams) if cams else None
89+
90+
env_rel = RCSFR3MultiEnvCreator()(
91+
name2ip=ROBOT2IP,
92+
camera_set=camera_set,
93+
robot_cfg=default_fr3_hw_robot_cfg(async_control=True),
94+
control_mode=config.operator_class.control_mode[0],
95+
gripper_cfg=default_fr3_hw_gripper_cfg(async_control=True),
96+
max_relative_movement=(
97+
0.5 if config.operator_class.control_mode[0] == ControlMode.JOINTS else (0.5, np.deg2rad(90))
98+
),
99+
relative_to=config.operator_class.control_mode[1],
100+
robot2world=robot2world,
101+
)
102+
# env_rel = StorageWrapper(
103+
# env_rel, DATASET_PATH, INSTRUCTION, batch_size=32, max_rows_per_group=100, max_rows_per_file=1000
104+
# )
105+
operator = GelloOperator(config) if isinstance(config, GelloConfig) else QuestOperator(config)
106+
else:
107+
# FR3
108+
rcs.scenes["duo"] = rcs.Scene(
109+
mjcf_scene="/ssd_data/juelg/rcs_modern/rcs_models/output/fr3_duo_flexible.xml",
110+
mjcf_robot=rcs.scenes["fr3_simple_pick_up"].mjcf_robot,
111+
robot_type=common.RobotType.FR3,
112+
)
113+
114+
robot_cfg = default_sim_robot_cfg("duo", idx="")
115+
116+
# resolution = (256, 256)
117+
# cameras = {
118+
# cam: SimCameraConfig(
119+
# identifier=cam,
120+
# type=CameraType.fixed,
121+
# resolution_height=resolution[1],
122+
# resolution_width=resolution[0],
123+
# frame_rate=0,
124+
# )
125+
# for cam in ["side", "wrist"]
126+
# }
127+
128+
sim_cfg = SimConfig()
129+
sim_cfg.async_control = True
130+
env_rel = SimMultiEnvCreator()(
131+
name2id=ROBOT2ID,
132+
robot_cfg=robot_cfg,
133+
control_mode=GelloOperator.control_mode[0],
134+
gripper_cfg=default_sim_gripper_cfg(),
135+
# cameras=default_mujoco_cameraset_cfg(),
136+
max_relative_movement=0.5,
137+
relative_to=GelloOperator.control_mode[1],
138+
sim_cfg=sim_cfg,
139+
robot2world=robot2world,
140+
)
141+
# sim = env_rel.unwrapped.envs[ROBOT2IP.keys().__iter__().__next__()].sim # type: ignore
142+
sim = env_rel.get_wrapper_attr("sim")
143+
operator = GelloOperator(config, sim) if isinstance(config, GelloConfig) else QuestOperator(config, sim)
144+
sim.open_gui()
145+
MujocoPublisher(sim.model, sim.data, MQ3_ADDR, visible_geoms_groups=list(range(1, 3)))
146+
return env_rel, operator
147+
148+
149+
def main():
150+
env_rel, operator = get_env()
151+
env_rel.reset()
152+
tele = TeleopLoop(env_rel, operator)
153+
with env_rel, tele: # type: ignore
154+
tele.environment_step_loop()
155+
156+
157+
if __name__ == "__main__":
158+
main()

0 commit comments

Comments
 (0)