Skip to content

Commit 670f2a8

Browse files
Merge pull request #641 from xieleo5/support_multi_camera_opencv
modify opencv renderer to support multiple camera view
2 parents 76df718 + 6e7cf77 commit 670f2a8

11 files changed

Lines changed: 195 additions & 59 deletions

File tree

robosuite/demos/demo_gripper_interaction.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from robosuite.models.arenas.table_arena import TableArena
1515
from robosuite.models.grippers import PandaGripper, RethinkGripper
1616
from robosuite.models.objects import BoxObject
17-
from robosuite.utils import OpenCVRenderer
17+
from robosuite.renderers.viewer import OpenCVViewer
1818
from robosuite.utils.binding_utils import MjRenderContextOffscreen, MjSim
1919
from robosuite.utils.mjcf_utils import new_actuator, new_joint
2020

@@ -66,7 +66,7 @@
6666
model = world.get_model(mode="mujoco")
6767

6868
sim = MjSim(model)
69-
viewer = OpenCVRenderer(sim)
69+
viewer = OpenCVViewer(sim)
7070
render_context = MjRenderContextOffscreen(sim, device_id=-1)
7171
sim.add_render_context(render_context)
7272

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import time
2+
3+
from robosuite.robots import MobileRobot
4+
from robosuite.utils.input_utils import *
5+
6+
MAX_FR = 25 # max frame rate for running simluation
7+
8+
if __name__ == "__main__":
9+
10+
# Create dict to hold options that will be passed to env creation call
11+
options = {}
12+
13+
# print welcome info
14+
print("Welcome to robosuite v{}!".format(suite.__version__))
15+
print(suite.__logo__)
16+
17+
# Choose environment and add it to options
18+
options["env_name"] = choose_environment()
19+
20+
# If a multi-arm environment has been chosen, choose configuration and appropriate robot(s)
21+
if "TwoArm" in options["env_name"]:
22+
# Choose env config and add it to options
23+
options["env_configuration"] = choose_multi_arm_config()
24+
25+
# If chosen configuration was bimanual, the corresponding robot must be Baxter. Else, have user choose robots
26+
if options["env_configuration"] == "single-robot":
27+
options["robots"] = choose_robots(exclude_bimanual=False, use_humanoids=True, exclude_single_arm=True)
28+
else:
29+
options["robots"] = []
30+
31+
# Have user choose two robots
32+
for i in range(2):
33+
print("Please choose Robot {}...\n".format(i))
34+
options["robots"].append(choose_robots(exclude_bimanual=False, use_humanoids=True))
35+
# If a humanoid environment has been chosen, choose humanoid robots
36+
elif "Humanoid" in options["env_name"]:
37+
options["robots"] = choose_robots(use_humanoids=True)
38+
else:
39+
options["robots"] = choose_robots(exclude_bimanual=False, use_humanoids=True)
40+
41+
# initialize the task
42+
env = suite.make(
43+
**options,
44+
has_renderer=True,
45+
has_offscreen_renderer=False,
46+
ignore_done=True,
47+
use_camera_obs=False,
48+
control_freq=20,
49+
renderer="mujoco",
50+
)
51+
env.reset()
52+
53+
camera_name = ["agentview", "birdview"]
54+
env.viewer.set_camera(camera_name=camera_name)
55+
for robot in env.robots:
56+
if isinstance(robot, MobileRobot):
57+
robot.enable_parts(legs=False, base=False)
58+
59+
# do visualization
60+
for i in range(10000):
61+
start = time.time()
62+
action = np.random.randn(*env.action_spec[0].shape)
63+
obs, reward, done, _ = env.step(action)
64+
env.render()
65+
66+
# limit frame rate if necessary
67+
elapsed = time.time() - start
68+
diff = 1 / MAX_FR - elapsed
69+
if diff > 0:
70+
time.sleep(diff)

robosuite/environments/base.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
import robosuite.macros as macros
1010
import robosuite.utils.sim_utils as SU
1111
from robosuite.renderers.base import load_renderer_config
12-
from robosuite.utils import OpenCVRenderer, SimulationError, XMLError
12+
from robosuite.renderers.viewer import OpenCVViewer
13+
from robosuite.utils import SimulationError, XMLError
1314
from robosuite.utils.binding_utils import MjRenderContextOffscreen, MjSim
1415

1516
REGISTERED_ENVS = {}
@@ -175,7 +176,7 @@ def initialize_renderer(self):
175176
if self.renderer == "mujoco":
176177
pass
177178
elif self.renderer == "mjviewer":
178-
from robosuite.renderers.mjviewer.mjviewer_renderer import MjviewerRenderer
179+
from robosuite.renderers.viewer import MjviewerRenderer
179180

180181
if self.render_camera is not None:
181182
camera_id = self.sim.model.camera_name2id(self.render_camera)
@@ -318,7 +319,7 @@ def _reset_internal(self):
318319
# create visualization screen or renderer
319320
if self.has_renderer and self.viewer is None:
320321
if self.renderer == "mujoco":
321-
self.viewer = OpenCVRenderer(self.sim)
322+
self.viewer = OpenCVViewer(self.sim)
322323

323324
# Set the camera angle for viewing
324325
if self.render_camera is not None:

robosuite/models/grippers/gripper_tester.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from robosuite.models.arenas.table_arena import TableArena
1010
from robosuite.models.objects import BoxObject
1111
from robosuite.models.world import MujocoWorldBase
12-
from robosuite.utils import OpenCVRenderer
12+
from robosuite.renderers.viewer import OpenCVViewer
1313
from robosuite.utils.binding_utils import MjRenderContextOffscreen, MjSim
1414
from robosuite.utils.mjcf_utils import array_to_string, new_actuator, new_joint
1515

@@ -116,7 +116,7 @@ def start_simulation(self):
116116
self.sim = MjSim.from_xml_string(model_xml)
117117

118118
if self.render:
119-
self.viewer = OpenCVRenderer(self.sim)
119+
self.viewer = OpenCVViewer(self.sim)
120120
# We also need to add the offscreen context
121121
if self.sim._render_context_offscreen is None:
122122
render_context = MjRenderContextOffscreen(self.sim, device_id=-1)

robosuite/renderers/mjviewer/__init__.py

Whitespace-only changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .mjviewer_renderer import MjviewerRenderer
2+
from .opencv_renderer import OpenCVViewer
File renamed without changes.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""
2+
opencv renderer class.
3+
"""
4+
import platform
5+
6+
import cv2
7+
import numpy as np
8+
9+
10+
class OpenCVViewer:
11+
def __init__(self, sim):
12+
# TODO: update this appropriately - need to get screen dimensions
13+
self.width = 1280
14+
self.height = 800
15+
16+
self.sim = sim
17+
self.set_camera(camera_id=0)
18+
self._window_name = "offscreen render"
19+
self._has_window = False
20+
self.keypress_callback = None
21+
22+
def set_camera(self, camera_id=None, camera_name=None, width=None, height=None):
23+
"""
24+
Set the camera view to the specified camera ID.
25+
26+
Args:
27+
camera_id (int or list): id(s) of the camera to set the current viewer to
28+
camera_name (str or list or None): name(s) of the camera to set the current viewer to
29+
"""
30+
31+
# enforce exactly one arg
32+
assert (camera_id is not None) or (camera_name is not None)
33+
assert (camera_id is None) or (camera_name is None)
34+
35+
# set width and height
36+
if width is not None:
37+
self.width = width
38+
if height is not None:
39+
self.height = height
40+
41+
if camera_id is not None:
42+
if isinstance(camera_id, int):
43+
camera_id = [camera_id]
44+
self.camera_names = [self.sim.model.camera_id2name(cam_id) for cam_id in camera_id]
45+
else:
46+
if isinstance(camera_name, str):
47+
camera_name = [camera_name]
48+
self.camera_names = list(camera_name)
49+
50+
def render(self):
51+
# get frame with offscreen renderer (assumes that the renderer already exists)
52+
im = [
53+
self.sim.render(camera_name=cam_name, height=self.height, width=self.width)[..., ::-1]
54+
for cam_name in self.camera_names
55+
]
56+
im = np.concatenate(im, axis=1) # concatenate horizontally
57+
58+
# write frame to window
59+
im = np.flip(im, axis=0)
60+
cv2.imshow(self._window_name, im)
61+
if (platform.system() != "Darwin") and (not self._has_window):
62+
# move window to top left of screen, and ensure we only move window on creation
63+
cv2.moveWindow(self._window_name, 0, 0)
64+
key = cv2.waitKey(1)
65+
if self.keypress_callback is not None:
66+
self.keypress_callback(key)
67+
self._has_window = True
68+
69+
def add_keypress_callback(self, keypress_callback):
70+
self.keypress_callback = keypress_callback
71+
72+
def close_window(self):
73+
"""
74+
Helper method to close the active window.
75+
"""
76+
if self._has_window:
77+
cv2.destroyWindow(self._window_name)
78+
cv2.waitKey(1)
79+
self._has_window = False
80+
81+
def close(self):
82+
"""
83+
Any cleanup to close renderer.
84+
"""
85+
86+
# NOTE: assume that @sim will get cleaned up outside the renderer - just delete the reference
87+
self.sim = None
88+
89+
# close window
90+
self.close_window()

robosuite/utils/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
11
from .errors import robosuiteError, XMLError, SimulationError, RandomizationError
2-
3-
from .opencv_renderer import OpenCVRenderer

robosuite/utils/opencv_renderer.py

Lines changed: 0 additions & 50 deletions
This file was deleted.

0 commit comments

Comments
 (0)