@@ -91,6 +91,7 @@ def __init__(
9191 self ._is_built = False
9292 self ._attached_link = None
9393 self ._attached_offset_T = None
94+ self ._attached_env_idx = None
9495
9596 self ._in_recording = False
9697 self ._recorded_imgs = []
@@ -124,7 +125,7 @@ def _build(self):
124125 self ._is_built = True
125126 self .set_pose (self ._transform , self ._pos , self ._lookat , self ._up )
126127
127- def attach (self , rigid_link , offset_T ):
128+ def attach (self , rigid_link , offset_T , env_idx : int | None = None ):
128129 """
129130 Attach the camera to a rigid link in the scene.
130131
@@ -136,9 +137,23 @@ def attach(self, rigid_link, offset_T):
136137 The rigid link to which the camera should be attached.
137138 offset_T : np.ndarray, shape (4, 4)
138139 The transformation matrix specifying the camera's pose relative to the rigid link.
140+ env_idx : int
141+ The environment index this camera should be tied to. Offsets the `offset_T` accordingly. Must be specified if running parallel environments
142+
143+ Raises
144+ ------
145+ Exception
146+ If running parallel simulations but env_idx is not specified.
139147 """
140148 self ._attached_link = rigid_link
141149 self ._attached_offset_T = offset_T
150+ if self ._visualizer ._scene .n_envs > 0 and env_idx is None :
151+ gs .raise_exception ("Must specify env_idx when running parallel simulations" )
152+ if env_idx is not None :
153+ n_envs = self ._visualizer ._scene .n_envs
154+ if env_idx >= n_envs :
155+ gs .raise_exception (f"Invalid env_idx { env_idx } for camera, configured for { n_envs } environments" )
156+ self ._attached_env_idx = env_idx
142157
143158 def detach (self ):
144159 """
@@ -148,6 +163,7 @@ def detach(self):
148163 """
149164 self ._attached_link = None
150165 self ._attached_offset_T = None
166+ self ._attached_env_idx = None
151167
152168 @gs .assert_built
153169 def move_to_attach (self ):
@@ -160,16 +176,15 @@ def move_to_attach(self):
160176 ------
161177 Exception
162178 If the camera has not been mounted using `attach()`.
163- Exception
164- If the simulation is running in parallel (`n_envs > 0`), which is currently unsupported for mounted cameras.
165179 """
166180 if self ._attached_link is None :
167181 gs .raise_exception (f"The camera hasn't been mounted!" )
168- if self ._visualizer ._scene .n_envs > 0 :
169- gs .raise_exception (f"Mounted camera not supported in parallel simulation!" )
170182
171- link_pos = self ._attached_link .get_pos ().cpu ().numpy ()
172- link_quat = self ._attached_link .get_quat ().cpu ().numpy ()
183+ link_pos = self ._attached_link .get_pos (envs_idx = self ._attached_env_idx ).cpu ().numpy ()
184+ link_quat = self ._attached_link .get_quat (envs_idx = self ._attached_env_idx ).cpu ().numpy ()
185+ if self ._attached_env_idx is not None :
186+ link_pos = link_pos [0 ] + self ._visualizer ._scene .envs_offset [self ._attached_env_idx ]
187+ link_quat = link_quat [0 ]
173188 link_T = gu .trans_quat_to_T (link_pos , link_quat )
174189 transform = link_T @ self ._attached_offset_T
175190 self .set_pose (transform = transform )
0 commit comments