@@ -130,6 +130,7 @@ def __init__(
130130 self .n_worlds = n_worlds
131131 self .n_drones = n_drones
132132 self .freq = freq
133+ self .max_geom_pairs = - 1 if n_drones < 64 else 2 * n_drones
133134 self .max_visual_geom = 1000
134135
135136 # Initialize MuJoCo world and data
@@ -287,6 +288,7 @@ def build_mjx_spec(self) -> mujoco.MjSpec:
287288 assert self ._xml_path .exists (), f"Model file { self ._xml_path } does not exist"
288289 spec = mujoco .MjSpec .from_file (str (self ._xml_path ))
289290 spec .option .timestep = 1 / self .freq
291+ spec .add_numeric (name = "max_geom_pairs" , data = [self .max_geom_pairs ])
290292 spec .copy_during_attach = True
291293 drone_spec = mujoco .MjSpec .from_file (str (self .drone_path ))
292294 frame = spec .worldbody .add_frame (name = "world" )
@@ -502,6 +504,7 @@ def build_mjx(self):
502504 if self .viewer is not None :
503505 self .viewer .close ()
504506 self .viewer = None
507+ self .spec .numeric ("max_geom_pairs" ).data = [self .max_geom_pairs ]
505508 self .mj_model , self .mj_data , self .mjx_model , self .mjx_data = self .build_mjx_model (self .spec )
506509
507510 def init_data (
@@ -573,11 +576,22 @@ def controllable(self) -> Array:
573576 def contacts (self , body : str | None = None ) -> Array :
574577 """Get contact information from the simulation.
575578
579+ Note:
580+ ``sim.max_geom_pairs`` limits the maximum detectable collision contacts per collision
581+ group. This is relevant for swarms, where the full pairwise collision buffer grows
582+ quadratically. By default, we allocate 2*n_drones contact pairs if the swarm size
583+ exceeds 64. That gives us enough capacity to detect all drone-drone contacts. However,
584+ if the swarm collapses e.g. into a single position, this will no longer be correct. If
585+ you need to truly detect all contacts, set ``sim.max_geom_pairs`` to -1 and rebuild the
586+ simulation.
587+
576588 Args:
577589 body: Optional body name to filter contacts for. If None, returns flags for all bodies.
578590
579591 Returns:
580- An boolean array of shape (n_worlds,) that is True if any contact is present.
592+ A boolean array of shape (n_worlds, n_contacts), one flag per slot in the contact
593+ buffer. Which geoms a slot holds is given by the matching entries of
594+ ``sim.mjx_data._impl.contact.geom1`` and ``geom2``.
581595 """
582596 if body is None :
583597 return self .mjx_data ._impl .contact .dist < 0
0 commit comments